diff --git a/MultipleImagePicker.podspec b/MultipleImagePicker.podspec index 69236a23..2ee84543 100644 --- a/MultipleImagePicker.podspec +++ b/MultipleImagePicker.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| } - s.dependency "HXPhotoPicker/Picker/Lite", "4.2.3" + s.dependency "HXPhotoPicker/Picker", "4.2.3" s.dependency "HXPhotoPicker/Editor/Lite", "4.2.3" s.pod_target_xcconfig = { diff --git a/README.md b/README.md index 890d36ab..f738357f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ React Native Multiple Image Picker **(RNMIP)** enables application to pick image ## Documentation 📚 - ## Features 🔥 | 🤩 | ![Logo][Logo] | @@ -78,10 +77,10 @@ const onPicker = async () => { - [x] Multiple Crop Image. - [x] Multiple Preview Image. - [x] Dynamic Theme. -- [x] Dynamic Language. -- [ ] Open Camera Controller. +- [x] Dynamic Language - [x] Open Crop Controller. -- [ ] Open Preview Controller. +- [x] Open Preview Controller. +- [ ] Open Camera Controller. ## Sponsor & Support ☕️ diff --git a/android/build.gradle b/android/build.gradle index cfca80cd..3e24239a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -135,6 +135,9 @@ dependencies { // simple camerax library (Not necessary) implementation 'io.github.lucksiege:camerax:v3.11.2' + // exoplayer + implementation "com.google.android.exoplayer:exoplayer:2.19.1" + implementation "com.facebook.react:react-native:+" diff --git a/android/src/main/java/com/margelo/nitro/multipleimagepicker/ExoPlayerEngine.kt b/android/src/main/java/com/margelo/nitro/multipleimagepicker/ExoPlayerEngine.kt new file mode 100644 index 00000000..ff0fd067 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/multipleimagepicker/ExoPlayerEngine.kt @@ -0,0 +1,126 @@ +package com.margelo.nitro.multipleimagepicker + +import android.content.Context +import android.net.Uri +import android.view.View +import com.google.android.exoplayer2.ExoPlayer +import com.google.android.exoplayer2.MediaItem +import com.google.android.exoplayer2.PlaybackException +import com.google.android.exoplayer2.Player +import com.google.android.exoplayer2.ui.StyledPlayerView +import com.luck.picture.lib.config.PictureMimeType +import com.luck.picture.lib.config.SelectorProviders +import com.luck.picture.lib.engine.VideoPlayerEngine +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnPlayerListener +import java.io.File +import java.util.concurrent.CopyOnWriteArrayList + + +class ExoPlayerEngine : VideoPlayerEngine { + private val listeners = CopyOnWriteArrayList() + + override fun onCreateVideoPlayer(context: Context): View { + val exoPlayer = StyledPlayerView(context) + exoPlayer.useController = true + return exoPlayer + } + + override fun onStarPlayer(exoPlayer: StyledPlayerView, media: LocalMedia) { + val player = exoPlayer.player + if (player != null) { + val mediaItem: MediaItem + val path = media.availablePath + mediaItem = if (PictureMimeType.isContent(path)) { + MediaItem.fromUri(Uri.parse(path)) + } else if (PictureMimeType.isHasHttp(path)) { + MediaItem.fromUri(path) + } else { + MediaItem.fromUri(Uri.fromFile(File(path))) + } + val config = SelectorProviders.getInstance().selectorConfig + player.repeatMode = + if (config.isLoopAutoPlay) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF + player.setMediaItem(mediaItem) + player.prepare() + player.play() + } + } + + override fun onResume(exoPlayer: StyledPlayerView) { + val player = exoPlayer.player + player?.play() + } + + override fun onPause(exoPlayer: StyledPlayerView) { + val player = exoPlayer.player + player?.pause() + } + + override fun isPlaying(exoPlayer: StyledPlayerView): Boolean { + val player = exoPlayer.player + return player != null && player.isPlaying + } + + + override fun addPlayListener(playerListener: OnPlayerListener) { + if (!listeners.contains(playerListener)) { + listeners.add(playerListener) + } + } + + override fun removePlayListener(playerListener: OnPlayerListener) { + listeners.remove(playerListener) + } + + override fun onPlayerAttachedToWindow(exoPlayer: StyledPlayerView) { + val player: Player = ExoPlayer.Builder(exoPlayer.context).build() + exoPlayer.player = player + player.addListener(mPlayerListener) + } + + override fun onPlayerDetachedFromWindow(exoPlayer: StyledPlayerView) { + val player = exoPlayer.player + if (player != null) { + player.removeListener(mPlayerListener) + player.release() + exoPlayer.player = null + } + } + + override fun destroy(exoPlayer: StyledPlayerView) { + val player = exoPlayer.player + if (player != null) { + player.removeListener(mPlayerListener) + player.release() + } + } + + private val mPlayerListener: Player.Listener = object : Player.Listener { + override fun onPlayerError(error: PlaybackException) { + for (i in listeners.indices) { + val playerListener = listeners[i] + playerListener.onPlayerError() + } + } + + override fun onPlaybackStateChanged(playbackState: Int) { + if (playbackState == Player.STATE_READY) { + for (i in listeners.indices) { + val playerListener = listeners[i] + playerListener.onPlayerReady() + } + } else if (playbackState == Player.STATE_BUFFERING) { + for (i in listeners.indices) { + val playerListener = listeners[i] + playerListener.onPlayerLoading() + } + } else if (playbackState == Player.STATE_ENDED) { + for (i in listeners.indices) { + val playerListener = listeners[i] + playerListener.onPlayerEnd() + } + } + } + } +} \ No newline at end of file diff --git a/android/src/main/java/com/margelo/nitro/multipleimagepicker/LoadingDialog.kt b/android/src/main/java/com/margelo/nitro/multipleimagepicker/LoadingDialog.kt new file mode 100644 index 00000000..84842c72 --- /dev/null +++ b/android/src/main/java/com/margelo/nitro/multipleimagepicker/LoadingDialog.kt @@ -0,0 +1,32 @@ +package com.margelo.nitro.multipleimagepicker + + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import android.view.Gravity +import android.view.ViewGroup + + +class LoadingDialog(context: Context?) : + Dialog(context!!, R.style.Picture_Theme_AlertDialog) { + init { + setCancelable(true) + setCanceledOnTouchOutside(false) + } + + override fun onCreate(savedInstanceState: Bundle) { + super.onCreate(savedInstanceState) + setContentView(R.layout.loading_dialog) + setDialogSize() + } + + private fun setDialogSize() { + val params = window!!.attributes + params.width = ViewGroup.LayoutParams.WRAP_CONTENT + params.height = ViewGroup.LayoutParams.WRAP_CONTENT + params.gravity = Gravity.CENTER + window!!.setWindowAnimations(R.style.PictureThemeDialogWindowStyle) + window!!.attributes = params + } +} \ No newline at end of file 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 84c2be77..fcd39e52 100644 --- a/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePicker.kt +++ b/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePicker.kt @@ -23,9 +23,15 @@ class MultipleImagePicker : HybridMultipleImagePickerSpec() { resolved: (result: CropResult) -> Unit, rejected: (reject: Double) -> Unit ) { - pickerModule.openCrop(image, config, resolved, rejected) } + override fun openPreview( + media: Array, + index: Double, + config: NitroPreviewConfig + ) { + pickerModule.openPreview(media, index.toInt(), config) + } } \ 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 fd8d5b83..f987d745 100644 --- a/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePickerImp.kt +++ b/android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePickerImp.kt @@ -19,6 +19,7 @@ import com.luck.picture.lib.basic.PictureSelector import com.luck.picture.lib.config.PictureMimeType import com.luck.picture.lib.config.SelectMimeType import com.luck.picture.lib.config.SelectModeConfig +import com.luck.picture.lib.engine.ImageEngine import com.luck.picture.lib.engine.PictureSelectorEngine import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.interfaces.OnMediaEditInterceptListener @@ -92,11 +93,13 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : val isMultiple = config.selectMode == SelectMode.MULTIPLE val selectMode = if (isMultiple) SelectModeConfig.MULTIPLE else SelectModeConfig.SINGLE - 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(config.crop) // Disabled force crop engine for multiple @@ -123,18 +126,27 @@ 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(config.language)) + .isPreviewFullScreenMode(true) .forResult(object : OnResultCallbackListener { override fun onResult(localMedia: ArrayList?) { var data: Array = arrayOf() @@ -168,18 +180,6 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : 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( @@ -211,17 +211,12 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : Uri.fromFile(file) } - - else -> { - Uri.parse(image) - } + 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 @@ -267,8 +262,23 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) : } } - private fun getLanguage(): Int { - return when (config.language) { + @ReactMethod + fun openPreview(media: Array, config: NitroPreviewConfig) { + + val imageEngine = GlideEngine.createGlideEngine() + + var list: ArrayList = arrayListOf() + + PictureSelector + .create(currentActivity) + .openPreview() + .setImageEngine(imageEngine) + .setLanguage(getLanguage(config.language)) + .startFragmentPreview(config.index.toInt(), false, list) + } + + private fun getLanguage(language: Language): Int { + return when (language) { Language.VI -> LanguageConfig.VIETNAM // -> 🇻🇳 My country. Yeahhh Language.EN -> LanguageConfig.ENGLISH Language.ZH_HANS -> LanguageConfig.CHINESE diff --git a/android/src/main/res/anim/anim_modal_in.xml b/android/src/main/res/anim/anim_modal_in.xml new file mode 100644 index 00000000..1eb14519 --- /dev/null +++ b/android/src/main/res/anim/anim_modal_in.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/android/src/main/res/layout/loading_dialog.xml b/android/src/main/res/layout/loading_dialog.xml new file mode 100644 index 00000000..b0204897 --- /dev/null +++ b/android/src/main/res/layout/loading_dialog.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/docs/docs/PREVIEW.mdx b/docs/docs/PREVIEW.mdx new file mode 100644 index 00000000..2482ea14 --- /dev/null +++ b/docs/docs/PREVIEW.mdx @@ -0,0 +1,79 @@ +--- +id: preview +title: Open Preview +sidebar_label: Open Preview +slug: /preview +--- + +import ReactPlayer from 'react-player' + +## Overview + +Preview is a simple image viewer that supports both local and remote media. It allows you to preview images and videos seamlessly. + + + +## Usage + +```typescript +import { openPreview } from '@baronha/react-native-multiple-image-picker' + +const cropConfig: CropConfig = { + language: 'system', +} + +const media: MediaPreview[] = [ + // remote image + { + path: 'https://images.unsplash.com/photo-1733235015085-fc29c17c4a16?w=500', + type: 'image', + }, + // local video + { + path: 'file://Documents/video-sample/mp4', + thumbnail: + 'https://images.unsplash.com/photo-1733889886752-f4599c954608?q=80&w=2574&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + type: 'video', + }, + // remote video + { + path: 'https://cdn.pixabay.com/video/2024/02/09/199958-911694865_large.mp4', + type: 'video', + }, +] + +// call to preview +openPreview(media, 2, cropConfig) +``` + +## `MediaPreview[]` + +`MediaPreview[]` is an array of media objects that you want to preview. Each object in the array should have the following properties:
+ +**NOTE**: You can also use [`Result[]`](/result) from openPicker's return result + +- `path`: A string representing the URL or file path of the media. +- `type`: A string indicating the type of media, either `image` or `video`. +- `thumbnail` (optional): A string representing the URL of the thumbnail image for videos. +- `localIdentifier` (optional): A string representing the local identifier for media from device gallery. + +## `index` + +The `index` parameter in the `openPreview` function specifies the initial media item to display. It is a zero-based index, meaning that `0` will display the first item in the `MediaPreview` array. + +## `cropConfig` + +### [`language`](/config/#language) + +## Additional Information + +- Ensure that the media paths are accessible and correctly formatted. +- The `openPreview` function is part of the `@baronha/react-native-multiple-image-picker` package, which should be installed and properly configured in your project. + +For more detailed information, refer to the [official documentation](https://github.com/baronha/react-native-multiple-image-picker). diff --git a/docs/sidebars.ts b/docs/sidebars.ts index 9746608a..5b7b92da 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -25,6 +25,7 @@ const sidebars: SidebarsConfig = { }, 'crop', + 'preview', ], } diff --git a/example/src/index.tsx b/example/src/index.tsx index f4a6360d..8edd0ae7 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -21,6 +21,7 @@ import { defaultOptions, Config, openCropper, + openPreview, } from '@baronha/react-native-multiple-image-picker' import { useImmer } from 'use-immer' import { StatusBar } from 'expo-status-bar' @@ -73,8 +74,8 @@ export default function App() { }) } - const onPressImage = () => { - // + const onPressImage = (_: Result, index: number) => { + openPreview(images, index, {}) } const onPicker = async () => { @@ -123,7 +124,7 @@ export default function App() { } const onRemovePhoto = (_: Result, index: number) => { - const data = [...images].filter((_, idx) => idx !== index) + const data = [...images].filter((__, idx) => idx !== index) setImages(data) } diff --git a/ios/HybridMultipleImagePicker+Crop.swift b/ios/HybridMultipleImagePicker+Crop.swift index 4cde2f2d..9eb2d3ae 100644 --- a/ios/HybridMultipleImagePicker+Crop.swift +++ b/ios/HybridMultipleImagePicker+Crop.swift @@ -6,18 +6,11 @@ // 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) @@ -104,14 +97,3 @@ extension HybridMultipleImagePicker { 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/HybridMultipleImagePicker+Preview.swift b/ios/HybridMultipleImagePicker+Preview.swift new file mode 100644 index 00000000..69d33183 --- /dev/null +++ b/ios/HybridMultipleImagePicker+Preview.swift @@ -0,0 +1,73 @@ +// +// HybridMultipleImagePicker+Preview.swift +// Pods +// +// Created by BAO HA on 11/12/24. +// + +import HXPhotoPicker + +extension HybridMultipleImagePicker { + func openPreview(media: [MediaPreview], index: Double, config: NitroPreviewConfig) throws { + var previewConfig = HXPhotoPicker.PhotoBrowser.Configuration() + previewConfig.showDelete = false + + var assets: [PhotoAsset] = [] + + previewConfig.tintColor = .white + previewConfig.videoPlayType = .auto + previewConfig.livePhotoPlayType = .auto + + previewConfig.languageType = setLocale(language: config.language) + + media.forEach { mediaItem in + + var asset: PhotoAsset? + + if let localIdentifier = mediaItem.localIdentifier { + asset = .init(localIdentifier: localIdentifier) + + // auto play gif + if let filePath = mediaItem.path, + let url = URL(string: filePath), isGifFile(url) == true + { + asset = .init(.init(imageURL: url)) + } + + } else if let path = mediaItem.path, let url = URL(string: path) { + let thumbnail = URL(string: mediaItem.thumbnail ?? "") ?? url + + if mediaItem.type == .image { + // network asset + if path.hasPrefix("https://") || path.hasPrefix("http://") { + asset = PhotoAsset(NetworkImageAsset( + thumbnailURL: thumbnail, + originalURL: url, + thumbnailLoadMode: .alwaysThumbnail, + originalLoadMode: .alwaysThumbnail + )) + + } else { + asset = .init(.init(imageURL: url)) + } + } else { + asset = .init(networkVideoAsset: .init(videoURL: url, coverImageURL: thumbnail)) + } + } + + if let asset { + assets.append(asset) + } + } + + if Int(index) > assets.count - 1 { return } + + DispatchQueue.main.async { + HXPhotoPicker.PhotoBrowser.show( + assets, + pageIndex: Int(index), + config: previewConfig + ) + } + } +} diff --git a/ios/Utils.swift b/ios/Utils.swift new file mode 100644 index 00000000..6550d11b --- /dev/null +++ b/ios/Utils.swift @@ -0,0 +1,36 @@ +// +// Utils.swift +// Pods +// +// Created by BAO HA on 11/12/24. +// + +import MobileCoreServices +import UniformTypeIdentifiers + +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) +} + +func isGifFile(_ url: URL) -> Bool { + // Kiểm tra phần mở rộng + if url.pathExtension.lowercased() == "gif" { + return true + } + + // Kiểm tra UTI + let fileExtension = url.pathExtension as CFString + guard let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, nil)?.takeRetainedValue() else { + return false + } + + return UTTypeConformsTo(uti, kUTTypeGIF) +} diff --git a/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.cpp b/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.cpp index 430b1ee1..6f5ea11a 100644 --- a/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.cpp @@ -35,6 +35,10 @@ namespace margelo::nitro::multipleimagepicker { enum class Presentation; } namespace margelo::nitro::multipleimagepicker { struct NitroCropConfig; } // Forward declaration of `CropResult` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct CropResult; } +// Forward declaration of `MediaPreview` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct MediaPreview; } +// Forward declaration of `NitroPreviewConfig` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct NitroPreviewConfig; } #include "NitroConfig.hpp" #include "JNitroConfig.hpp" @@ -71,6 +75,10 @@ namespace margelo::nitro::multipleimagepicker { struct CropResult; } #include "CropResult.hpp" #include "JFunc_void_CropResult.hpp" #include "JCropResult.hpp" +#include "MediaPreview.hpp" +#include "JMediaPreview.hpp" +#include "NitroPreviewConfig.hpp" +#include "JNitroPreviewConfig.hpp" namespace margelo::nitro::multipleimagepicker { @@ -101,5 +109,17 @@ namespace margelo::nitro::multipleimagepicker { 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)); } + void JHybridMultipleImagePickerSpec::openPreview(const std::vector& media, double index, const NitroPreviewConfig& config) { + static const auto method = _javaPart->getClass()->getMethod> /* media */, double /* index */, jni::alias_ref /* config */)>("openPreview"); + method(_javaPart, [&]() { + size_t __size = media.size(); + jni::local_ref> __array = jni::JArrayClass::newArray(__size); + for (size_t __i = 0; __i < __size; __i++) { + const auto& __element = media[__i]; + __array->setElement(__i, *JMediaPreview::fromCpp(__element)); + } + return __array; + }(), index, JNitroPreviewConfig::fromCpp(config)); + } } // namespace margelo::nitro::multipleimagepicker diff --git a/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.hpp b/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.hpp index b5a44f69..cb122563 100644 --- a/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridMultipleImagePickerSpec.hpp @@ -53,6 +53,7 @@ namespace margelo::nitro::multipleimagepicker { // 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; + void openPreview(const std::vector& media, double index, const NitroPreviewConfig& config) override; private: friend HybridBase; diff --git a/nitrogen/generated/android/c++/JMediaPreview.hpp b/nitrogen/generated/android/c++/JMediaPreview.hpp new file mode 100644 index 00000000..8019d0a9 --- /dev/null +++ b/nitrogen/generated/android/c++/JMediaPreview.hpp @@ -0,0 +1,67 @@ +/// +/// JMediaPreview.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 "MediaPreview.hpp" + +#include "JResultType.hpp" +#include "ResultType.hpp" +#include +#include + +namespace margelo::nitro::multipleimagepicker { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "MediaPreview" and the the Kotlin data class "MediaPreview". + */ + struct JMediaPreview final: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/multipleimagepicker/MediaPreview;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct MediaPreview by copying all values to C++. + */ + [[maybe_unused]] + MediaPreview toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldType = clazz->getField("type"); + jni::local_ref type = this->getFieldValue(fieldType); + static const auto fieldPath = clazz->getField("path"); + jni::local_ref path = this->getFieldValue(fieldPath); + static const auto fieldThumbnail = clazz->getField("thumbnail"); + jni::local_ref thumbnail = this->getFieldValue(fieldThumbnail); + static const auto fieldLocalIdentifier = clazz->getField("localIdentifier"); + jni::local_ref localIdentifier = this->getFieldValue(fieldLocalIdentifier); + return MediaPreview( + type->toCpp(), + path != nullptr ? std::make_optional(path->toStdString()) : std::nullopt, + thumbnail != nullptr ? std::make_optional(thumbnail->toStdString()) : std::nullopt, + localIdentifier != nullptr ? std::make_optional(localIdentifier->toStdString()) : 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 MediaPreview& value) { + return newInstance( + JResultType::fromCpp(value.type), + value.path.has_value() ? jni::make_jstring(value.path.value()) : nullptr, + value.thumbnail.has_value() ? jni::make_jstring(value.thumbnail.value()) : nullptr, + value.localIdentifier.has_value() ? jni::make_jstring(value.localIdentifier.value()) : nullptr + ); + } + }; + +} // namespace margelo::nitro::multipleimagepicker diff --git a/nitrogen/generated/android/c++/JNitroPreviewConfig.hpp b/nitrogen/generated/android/c++/JNitroPreviewConfig.hpp new file mode 100644 index 00000000..0e829f97 --- /dev/null +++ b/nitrogen/generated/android/c++/JNitroPreviewConfig.hpp @@ -0,0 +1,53 @@ +/// +/// JNitroPreviewConfig.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 "NitroPreviewConfig.hpp" + +#include "JLanguage.hpp" +#include "Language.hpp" + +namespace margelo::nitro::multipleimagepicker { + + using namespace facebook; + + /** + * The C++ JNI bridge between the C++ struct "NitroPreviewConfig" and the the Kotlin data class "NitroPreviewConfig". + */ + struct JNitroPreviewConfig final: public jni::JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/multipleimagepicker/NitroPreviewConfig;"; + + public: + /** + * Convert this Java/Kotlin-based struct to the C++ struct NitroPreviewConfig by copying all values to C++. + */ + [[maybe_unused]] + NitroPreviewConfig toCpp() const { + static const auto clazz = javaClassStatic(); + static const auto fieldLanguage = clazz->getField("language"); + jni::local_ref language = this->getFieldValue(fieldLanguage); + return NitroPreviewConfig( + language->toCpp() + ); + } + + 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 NitroPreviewConfig& value) { + return newInstance( + JLanguage::fromCpp(value.language) + ); + } + }; + +} // namespace margelo::nitro::multipleimagepicker 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 82e8d0c4..9e941926 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/HybridMultipleImagePickerSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/HybridMultipleImagePickerSpec.kt @@ -62,6 +62,10 @@ abstract class HybridMultipleImagePickerSpec: HybridObject() { val __result = openCrop(image, config, resolved.toLambda(), rejected.toLambda()) return __result } + + @DoNotStrip + @Keep + abstract fun openPreview(media: Array, index: Double, config: NitroPreviewConfig): Unit private external fun initHybrid(): HybridData diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/MediaPreview.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/MediaPreview.kt new file mode 100644 index 00000000..68196008 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/MediaPreview.kt @@ -0,0 +1,24 @@ +/// +/// MediaPreview.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 "MediaPreview". + */ +@DoNotStrip +@Keep +data class MediaPreview( + val type: ResultType, + val path: String?, + val thumbnail: String?, + val localIdentifier: String? +) diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/NitroPreviewConfig.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/NitroPreviewConfig.kt new file mode 100644 index 00000000..fc5785d6 --- /dev/null +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/multipleimagepicker/NitroPreviewConfig.kt @@ -0,0 +1,21 @@ +/// +/// NitroPreviewConfig.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 "NitroPreviewConfig". + */ +@DoNotStrip +@Keep +data class NitroPreviewConfig( + val language: Language +) diff --git a/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Bridge.hpp b/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Bridge.hpp index 7cc2cdd8..f6741986 100644 --- a/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Bridge.hpp +++ b/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Bridge.hpp @@ -14,6 +14,8 @@ namespace margelo::nitro::multipleimagepicker { struct CropRatio; } namespace margelo::nitro::multipleimagepicker { struct CropResult; } // Forward declaration of `HybridMultipleImagePickerSpec` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { class HybridMultipleImagePickerSpec; } +// Forward declaration of `MediaPreview` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct MediaPreview; } // Forward declaration of `PickerCropConfig` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct PickerCropConfig; } // Forward declaration of `ResultType` to properly resolve imports. @@ -31,6 +33,7 @@ namespace MultipleImagePicker { class HybridMultipleImagePickerSpecCxx; } #include "CropRatio.hpp" #include "CropResult.hpp" #include "HybridMultipleImagePickerSpec.hpp" +#include "MediaPreview.hpp" #include "PickerCropConfig.hpp" #include "Result.hpp" #include "ResultType.hpp" @@ -207,6 +210,17 @@ namespace margelo::nitro::multipleimagepicker::bridge::swift { return std::make_shared(value); } + // pragma MARK: std::vector + /** + * Specialized version of `std::vector`. + */ + using std__vector_MediaPreview_ = std::vector; + inline std::vector create_std__vector_MediaPreview_(size_t size) { + std::vector vector; + vector.reserve(size); + return vector; + } + // 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 76ebc917..01b4aca3 100644 --- a/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Umbrella.hpp +++ b/nitrogen/generated/ios/MultipleImagePicker-Swift-Cxx-Umbrella.hpp @@ -16,12 +16,16 @@ namespace margelo::nitro::multipleimagepicker { struct CropResult; } namespace margelo::nitro::multipleimagepicker { class HybridMultipleImagePickerSpec; } // Forward declaration of `Language` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { enum class Language; } +// Forward declaration of `MediaPreview` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct MediaPreview; } // Forward declaration of `MediaType` to properly resolve imports. 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 `NitroPreviewConfig` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct NitroPreviewConfig; } // Forward declaration of `PickerCropConfig` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct PickerCropConfig; } // Forward declaration of `Presentation` to properly resolve imports. @@ -44,9 +48,11 @@ namespace margelo::nitro::multipleimagepicker { enum class Theme; } #include "CropResult.hpp" #include "HybridMultipleImagePickerSpec.hpp" #include "Language.hpp" +#include "MediaPreview.hpp" #include "MediaType.hpp" #include "NitroConfig.hpp" #include "NitroCropConfig.hpp" +#include "NitroPreviewConfig.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 03fce052..df9896f6 100644 --- a/nitrogen/generated/ios/c++/HybridMultipleImagePickerSpecSwift.hpp +++ b/nitrogen/generated/ios/c++/HybridMultipleImagePickerSpecSwift.hpp @@ -40,6 +40,10 @@ namespace margelo::nitro::multipleimagepicker { enum class Presentation; } namespace margelo::nitro::multipleimagepicker { struct NitroCropConfig; } // Forward declaration of `CropResult` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct CropResult; } +// Forward declaration of `MediaPreview` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct MediaPreview; } +// Forward declaration of `NitroPreviewConfig` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct NitroPreviewConfig; } #include "NitroConfig.hpp" #include "MediaType.hpp" @@ -59,6 +63,8 @@ namespace margelo::nitro::multipleimagepicker { struct CropResult; } #include #include "NitroCropConfig.hpp" #include "CropResult.hpp" +#include "MediaPreview.hpp" +#include "NitroPreviewConfig.hpp" #if __has_include() #include @@ -109,6 +115,9 @@ namespace margelo::nitro::multipleimagepicker { 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); } + inline void openPreview(const std::vector& media, double index, const NitroPreviewConfig& config) override { + _swiftPart.openPreview(media, std::forward(index), config); + } private: MultipleImagePicker::HybridMultipleImagePickerSpecCxx _swiftPart; diff --git a/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpec.swift b/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpec.swift index b8d71d6e..f71ac75c 100644 --- a/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpec.swift +++ b/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpec.swift @@ -34,4 +34,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 + func openPreview(media: [MediaPreview], index: Double, config: NitroPreviewConfig) throws -> Void } diff --git a/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpecCxx.swift b/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpecCxx.swift index 4956d226..1be82311 100644 --- a/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpecCxx.swift +++ b/nitrogen/generated/ios/swift/HybridMultipleImagePickerSpecCxx.swift @@ -145,4 +145,15 @@ 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 openPreview(media: bridge.std__vector_MediaPreview_, index: Double, config: NitroPreviewConfig) -> Void { + do { + try self.__implementation.openPreview(media: media.map({ __item in __item }), index: index, config: config) + 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/MediaPreview.swift b/nitrogen/generated/ios/swift/MediaPreview.swift new file mode 100644 index 00000000..b8bd9991 --- /dev/null +++ b/nitrogen/generated/ios/swift/MediaPreview.swift @@ -0,0 +1,122 @@ +/// +/// MediaPreview.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 `MediaPreview`, backed by a C++ struct. + */ +public typealias MediaPreview = margelo.nitro.multipleimagepicker.MediaPreview + +public extension MediaPreview { + private typealias bridge = margelo.nitro.multipleimagepicker.bridge.swift + + /** + * Create a new instance of `MediaPreview`. + */ + init(type: ResultType, path: String?, thumbnail: String?, localIdentifier: String?) { + self.init(type, { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = path { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = thumbnail { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }(), { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = localIdentifier { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }()) + } + + var type: ResultType { + @inline(__always) + get { + return self.__type + } + @inline(__always) + set { + self.__type = newValue + } + } + + var path: String? { + @inline(__always) + get { + return { () -> String? in + if let __unwrapped = self.__path.value { + return String(__unwrapped) + } else { + return nil + } + }() + } + @inline(__always) + set { + self.__path = { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }() + } + } + + var thumbnail: String? { + @inline(__always) + get { + return { () -> String? in + if let __unwrapped = self.__thumbnail.value { + return String(__unwrapped) + } else { + return nil + } + }() + } + @inline(__always) + set { + self.__thumbnail = { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }() + } + } + + var localIdentifier: String? { + @inline(__always) + get { + return { () -> String? in + if let __unwrapped = self.__localIdentifier.value { + return String(__unwrapped) + } else { + return nil + } + }() + } + @inline(__always) + set { + self.__localIdentifier = { () -> bridge.std__optional_std__string_ in + if let __unwrappedValue = newValue { + return bridge.create_std__optional_std__string_(std.string(__unwrappedValue)) + } else { + return .init() + } + }() + } + } +} diff --git a/nitrogen/generated/ios/swift/NitroPreviewConfig.swift b/nitrogen/generated/ios/swift/NitroPreviewConfig.swift new file mode 100644 index 00000000..00fd3127 --- /dev/null +++ b/nitrogen/generated/ios/swift/NitroPreviewConfig.swift @@ -0,0 +1,35 @@ +/// +/// NitroPreviewConfig.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 `NitroPreviewConfig`, backed by a C++ struct. + */ +public typealias NitroPreviewConfig = margelo.nitro.multipleimagepicker.NitroPreviewConfig + +public extension NitroPreviewConfig { + private typealias bridge = margelo.nitro.multipleimagepicker.bridge.swift + + /** + * Create a new instance of `NitroPreviewConfig`. + */ + init(language: Language) { + self.init(language) + } + + var language: Language { + @inline(__always) + get { + return self.__language + } + @inline(__always) + set { + self.__language = newValue + } + } +} diff --git a/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.cpp b/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.cpp index cf4ed3a1..c6a90ba2 100644 --- a/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.cpp @@ -16,6 +16,7 @@ namespace margelo::nitro::multipleimagepicker { registerHybrids(this, [](Prototype& prototype) { prototype.registerHybridMethod("openPicker", &HybridMultipleImagePickerSpec::openPicker); prototype.registerHybridMethod("openCrop", &HybridMultipleImagePickerSpec::openCrop); + prototype.registerHybridMethod("openPreview", &HybridMultipleImagePickerSpec::openPreview); }); } diff --git a/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.hpp b/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.hpp index 436dabfe..9d5b4f3d 100644 --- a/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridMultipleImagePickerSpec.hpp @@ -21,6 +21,10 @@ namespace margelo::nitro::multipleimagepicker { struct Result; } namespace margelo::nitro::multipleimagepicker { struct NitroCropConfig; } // Forward declaration of `CropResult` to properly resolve imports. namespace margelo::nitro::multipleimagepicker { struct CropResult; } +// Forward declaration of `MediaPreview` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct MediaPreview; } +// Forward declaration of `NitroPreviewConfig` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { struct NitroPreviewConfig; } #include "NitroConfig.hpp" #include @@ -29,6 +33,8 @@ namespace margelo::nitro::multipleimagepicker { struct CropResult; } #include #include "NitroCropConfig.hpp" #include "CropResult.hpp" +#include "MediaPreview.hpp" +#include "NitroPreviewConfig.hpp" namespace margelo::nitro::multipleimagepicker { @@ -63,6 +69,7 @@ namespace margelo::nitro::multipleimagepicker { // 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; + virtual void openPreview(const std::vector& media, double index, const NitroPreviewConfig& config) = 0; protected: // Hybrid Setup diff --git a/nitrogen/generated/shared/c++/MediaPreview.hpp b/nitrogen/generated/shared/c++/MediaPreview.hpp new file mode 100644 index 00000000..9f11e38a --- /dev/null +++ b/nitrogen/generated/shared/c++/MediaPreview.hpp @@ -0,0 +1,83 @@ +/// +/// MediaPreview.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 `ResultType` to properly resolve imports. +namespace margelo::nitro::multipleimagepicker { enum class ResultType; } + +#include "ResultType.hpp" +#include +#include + +namespace margelo::nitro::multipleimagepicker { + + /** + * A struct which can be represented as a JavaScript object (MediaPreview). + */ + struct MediaPreview { + public: + ResultType type SWIFT_PRIVATE; + std::optional path SWIFT_PRIVATE; + std::optional thumbnail SWIFT_PRIVATE; + std::optional localIdentifier SWIFT_PRIVATE; + + public: + explicit MediaPreview(ResultType type, std::optional path, std::optional thumbnail, std::optional localIdentifier): type(type), path(path), thumbnail(thumbnail), localIdentifier(localIdentifier) {} + }; + +} // namespace margelo::nitro::multipleimagepicker + +namespace margelo::nitro { + + using namespace margelo::nitro::multipleimagepicker; + + // C++ MediaPreview <> JS MediaPreview (object) + template <> + struct JSIConverter { + static inline MediaPreview fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return MediaPreview( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "type")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "path")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "thumbnail")), + JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "localIdentifier")) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const MediaPreview& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, "type", JSIConverter::toJSI(runtime, arg.type)); + obj.setProperty(runtime, "path", JSIConverter>::toJSI(runtime, arg.path)); + obj.setProperty(runtime, "thumbnail", JSIConverter>::toJSI(runtime, arg.thumbnail)); + obj.setProperty(runtime, "localIdentifier", JSIConverter>::toJSI(runtime, arg.localIdentifier)); + 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, "type"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "path"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "thumbnail"))) return false; + if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "localIdentifier"))) return false; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/nitrogen/generated/shared/c++/NitroPreviewConfig.hpp b/nitrogen/generated/shared/c++/NitroPreviewConfig.hpp new file mode 100644 index 00000000..772e36e3 --- /dev/null +++ b/nitrogen/generated/shared/c++/NitroPreviewConfig.hpp @@ -0,0 +1,69 @@ +/// +/// NitroPreviewConfig.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; } + +#include "Language.hpp" + +namespace margelo::nitro::multipleimagepicker { + + /** + * A struct which can be represented as a JavaScript object (NitroPreviewConfig). + */ + struct NitroPreviewConfig { + public: + Language language SWIFT_PRIVATE; + + public: + explicit NitroPreviewConfig(Language language): language(language) {} + }; + +} // namespace margelo::nitro::multipleimagepicker + +namespace margelo::nitro { + + using namespace margelo::nitro::multipleimagepicker; + + // C++ NitroPreviewConfig <> JS NitroPreviewConfig (object) + template <> + struct JSIConverter { + static inline NitroPreviewConfig fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) { + jsi::Object obj = arg.asObject(runtime); + return NitroPreviewConfig( + JSIConverter::fromJSI(runtime, obj.getProperty(runtime, "language")) + ); + } + static inline jsi::Value toJSI(jsi::Runtime& runtime, const NitroPreviewConfig& arg) { + jsi::Object obj(runtime); + obj.setProperty(runtime, "language", JSIConverter::toJSI(runtime, arg.language)); + 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; + return true; + } + }; + +} // namespace margelo::nitro diff --git a/src/index.ts b/src/index.ts index bd3acf50..901c0671 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,9 @@ import { CropConfig, NitroCropConfig, CropRatio, + PreviewConfig, + NitroPreviewConfig, + MediaPreview, } from './types' import { CropError } from './types/error' @@ -69,6 +72,10 @@ export async function openCropper( ...config, } as NitroCropConfig + if (config?.language && !LANGUAGES.includes(config.language)) { + config.language = 'system' + } + Picker.openCrop( image, cropConfig, @@ -82,6 +89,31 @@ export async function openCropper( }) } +export function openPreview( + media: MediaPreview[] | Result[], + index: number = 0, + conf: PreviewConfig +): void { + const config: PreviewConfig = { + language: conf.language ?? 'system', + ...conf, + } + + if (config?.language && !LANGUAGES.includes(config.language)) { + config.language = 'system' + } + + if (media.length === 0) { + throw new Error('Media is required') + } + + Picker.openPreview( + media as MediaPreview[], + index, + config as NitroPreviewConfig + ) +} + const DEFAULT_COUNT = 20 export const DEFAULT_RATIO: CropRatio[] = [] diff --git a/src/specs/MultipleImagePicker.nitro.ts b/src/specs/MultipleImagePicker.nitro.ts index 267e7123..11bf82c7 100644 --- a/src/specs/MultipleImagePicker.nitro.ts +++ b/src/specs/MultipleImagePicker.nitro.ts @@ -1,5 +1,12 @@ import { type HybridObject } from 'react-native-nitro-modules' -import { CropResult, NitroConfig, NitroCropConfig, Result } from '../types' +import { + CropResult, + MediaPreview, + NitroConfig, + NitroCropConfig, + NitroPreviewConfig, + Result, +} from '../types' export interface MultipleImagePicker extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { @@ -15,4 +22,10 @@ export interface MultipleImagePicker resolved: (result: CropResult) => void, rejected: (reject: number) => void ): void + + openPreview( + media: MediaPreview[], + index: number, + config: NitroPreviewConfig + ): void } diff --git a/src/types/config.ts b/src/types/config.ts index 94d54791..c2bcdab2 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -472,6 +472,8 @@ export interface Config } } +// CROP + export interface NitroCropConfig extends PickerCropConfig { /** * Interface language @@ -528,3 +530,33 @@ export interface CropConfig */ ratio?: CropRatio[] } + +// PREVIEW +export type NitroPreviewConfig = { + language: Language +} + +export interface PreviewConfig + 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 +} diff --git a/src/types/index.ts b/src/types/index.ts index 9e6769c6..ac22cc34 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -2,3 +2,4 @@ // export * from './error' export * from './result' export * from './config' +export * from './media' diff --git a/src/types/media.ts b/src/types/media.ts new file mode 100644 index 00000000..f4cd5720 --- /dev/null +++ b/src/types/media.ts @@ -0,0 +1,8 @@ +import { ResultType } from './result' + +export interface MediaPreview { + type: ResultType + path?: string + thumbnail?: string + localIdentifier?: string +} diff --git a/src/types/result.ts b/src/types/result.ts index d8aa8bcd..9d60f693 100644 --- a/src/types/result.ts +++ b/src/types/result.ts @@ -1,4 +1,4 @@ -type ResultType = 'image' | 'video' +export type ResultType = 'image' | 'video' export interface Result { path: string