@imgly/editor-react-native 1.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/IMGLYEditorModule.podspec +21 -0
  2. package/LICENSE.md +137 -0
  3. package/README.md +31 -0
  4. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  5. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  6. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  7. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  8. package/android/.gradle/8.9/gc.properties +0 -0
  9. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  10. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  11. package/android/.gradle/vcs-1/gc.properties +0 -0
  12. package/android/build.gradle +65 -0
  13. package/android/src/main/AndroidManifest.xml +8 -0
  14. package/android/src/main/assets/photo-ui-empty.png +0 -0
  15. package/android/src/main/kotlin/ly/img/editor/reactnative/module/IMGLYEditorModule.kt +252 -0
  16. package/android/src/main/kotlin/ly/img/editor/reactnative/module/IMGLYEditorPackage.kt +35 -0
  17. package/android/src/main/kotlin/ly/img/editor/reactnative/module/activity/EditorActivity.kt +112 -0
  18. package/android/src/main/kotlin/ly/img/editor/reactnative/module/builder/EditorBuilder.kt +478 -0
  19. package/android/src/main/kotlin/ly/img/editor/reactnative/module/helper/AssetHelper.kt +18 -0
  20. package/android/src/main/kotlin/ly/img/editor/reactnative/module/helper/IntentHelper.kt +61 -0
  21. package/android/src/main/kotlin/ly/img/editor/reactnative/module/model/EditorPreset.kt +47 -0
  22. package/android/src/main/kotlin/ly/img/editor/reactnative/module/model/EditorResult.kt +73 -0
  23. package/android/src/main/kotlin/ly/img/editor/reactnative/module/model/EditorSettings.kt +63 -0
  24. package/android/src/main/kotlin/ly/img/editor/reactnative/module/model/EditorSourceType.kt +39 -0
  25. package/android/src/main/kotlin/ly/img/editor/reactnative/module/model/Source.kt +44 -0
  26. package/android/src/newarch/IMGLYEditorModuleSpec.kt +7 -0
  27. package/android/src/oldarch/IMGLYEditorModuleSpec.kt +18 -0
  28. package/index.d.ts +3 -0
  29. package/index.js +3 -0
  30. package/ios/Constants/IMGLYConstants.h +13 -0
  31. package/ios/Constants/IMGLYConstants.m +13 -0
  32. package/ios/Extensions/OnCreate+.swift +75 -0
  33. package/ios/Extensions/OnExport+.swift +64 -0
  34. package/ios/Extensions/String+.swift +7 -0
  35. package/ios/Helper/ExportHelper.swift +67 -0
  36. package/ios/IMGLYEditorModule-Bridging-Header.h +3 -0
  37. package/ios/IMGLYEditorModule.h +11 -0
  38. package/ios/IMGLYEditorModule.mm +86 -0
  39. package/ios/IMGLYEditorModule.swift +110 -0
  40. package/ios/Model/EditorBuilder.swift +293 -0
  41. package/ios/Model/EditorPreset.swift +52 -0
  42. package/ios/Model/EditorResult.swift +27 -0
  43. package/ios/Model/EditorSettings.swift +33 -0
  44. package/ios/Model/EditorSourceType.swift +11 -0
  45. package/ios/Model/Source.swift +8 -0
  46. package/ios/View/ModalEditor.swift +55 -0
  47. package/package.json +46 -0
  48. package/src/NativeIMGLYEditor.ts +195 -0
@@ -0,0 +1,47 @@
1
+ package ly.img.editor.reactnative.module.model
2
+
3
+ import android.os.Parcel
4
+ import android.os.Parcelable
5
+
6
+ /** An enum containing all prebuilt editor configurations. */
7
+ enum class EditorPreset(
8
+ val value: String,
9
+ ) : Parcelable {
10
+ /** The apparel editor. */
11
+ APPAREL("apparel"),
12
+
13
+ /** The postcard editor. */
14
+ POSTCARD("postcard"),
15
+
16
+ /** The photo editor. */
17
+ PHOTO("photo"),
18
+
19
+ /** The design editor. */
20
+ DESIGN("design"),
21
+
22
+ /** The video editor. */
23
+ VIDEO("video"),
24
+ ;
25
+
26
+ override fun toString(): String = value
27
+
28
+ override fun writeToParcel(
29
+ parcel: Parcel,
30
+ flags: Int,
31
+ ) {
32
+ parcel.writeString(value)
33
+ }
34
+
35
+ override fun describeContents(): Int = 0
36
+
37
+ companion object CREATOR : Parcelable.Creator<EditorPreset> {
38
+ override fun createFromParcel(parcel: Parcel): EditorPreset {
39
+ val value = parcel.readString()
40
+ return entries.find { it.value == value } ?: throw IllegalArgumentException("Invalid value for EditorPreset")
41
+ }
42
+
43
+ override fun newArray(size: Int): Array<EditorPreset?> = arrayOfNulls(size)
44
+
45
+ fun fromValue(value: String): EditorPreset? = entries.find { it.value == value }
46
+ }
47
+ }
@@ -0,0 +1,73 @@
1
+ package ly.img.editor.reactnative.module.model
2
+
3
+ import android.os.Parcel
4
+ import android.os.Parcelable
5
+
6
+ /**
7
+ * A class representing the result of an editor export.
8
+ * @property scene The source of the exported scene.
9
+ * @property artifact The source of the exported artifact, e.g. image, video.
10
+ * @property thumbnail The source of the exported thumbnail.
11
+ * @property metadata The associated metadata.
12
+ */
13
+ data class EditorResult(
14
+ val scene: String?,
15
+ val artifact: String?,
16
+ val thumbnail: String?,
17
+ val metadata: Map<String, Any> = emptyMap(),
18
+ ) : Parcelable {
19
+ /**
20
+ * Creates a new instance from a given [Parcel].
21
+ * @param parcel The [Parcel].
22
+ */
23
+ constructor(parcel: Parcel) : this(
24
+ parcel.readString(),
25
+ parcel.readString(),
26
+ parcel.readString(),
27
+ readMap(parcel),
28
+ )
29
+
30
+ override fun writeToParcel(
31
+ parcel: Parcel,
32
+ flags: Int,
33
+ ) {
34
+ parcel.writeString(scene)
35
+ parcel.writeString(artifact)
36
+ parcel.writeString(thumbnail)
37
+ writeMap(parcel, metadata, flags)
38
+ }
39
+
40
+ override fun describeContents(): Int = 0
41
+
42
+ companion object CREATOR : Parcelable.Creator<EditorResult> {
43
+ override fun createFromParcel(parcel: Parcel): EditorResult = EditorResult(parcel)
44
+
45
+ override fun newArray(size: Int): Array<EditorResult?> = arrayOfNulls(size)
46
+
47
+ private fun writeMap(
48
+ parcel: Parcel,
49
+ map: Map<String, Any>,
50
+ flags: Int,
51
+ ) {
52
+ parcel.writeInt(map.size)
53
+ for ((key, value) in map) {
54
+ parcel.writeString(key)
55
+ parcel.writeValue(value)
56
+ }
57
+ }
58
+
59
+ private fun readMap(parcel: Parcel): Map<String, Any> {
60
+ val size = parcel.readInt()
61
+ val map = mutableMapOf<String, Any>()
62
+ repeat(size) {
63
+ val key = parcel.readString()
64
+ val value = parcel.readValue(this::class.java.classLoader)
65
+ key ?: throw (IllegalStateException("Key must not be null."))
66
+ value ?: throw (IllegalStateException("Value must not be null."))
67
+
68
+ map[key] = value
69
+ }
70
+ return map
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,63 @@
1
+ package ly.img.editor.reactnative.module.model
2
+
3
+ import android.os.Parcel
4
+ import android.os.Parcelable
5
+
6
+ /**
7
+ * A class containing all necessary information to configure an editor.
8
+ * @property license The license key.
9
+ * @property baseUri The base uri for the assets included in the scene that have a relative source.
10
+ * @property assetBaseUri The base uri of the default assets in the asset library.
11
+ * @property userId The id of the current user.
12
+ * @property source The source to load into the editor.
13
+ */
14
+ data class EditorSettings(
15
+ val license: String,
16
+ val baseUri: String,
17
+ val assetBaseUri: String?,
18
+ val userId: String?,
19
+ var source: Source?,
20
+ ) : Parcelable {
21
+ /**
22
+ * Creates a new instance from a given [Parcel].
23
+ * @param parcel The [Parcel].
24
+ */
25
+ constructor(parcel: Parcel) : this(
26
+ parcel.readString() ?: throw (Exception("Missing value for key 'license.'")),
27
+ parcel.readString() ?: throw (Exception("Missing value for key 'baseUri.'")),
28
+ parcel.readString(),
29
+ parcel.readString(),
30
+ parcel.readParcelable(Source::class.java.classLoader),
31
+ )
32
+
33
+ override fun writeToParcel(
34
+ parcel: Parcel,
35
+ flags: Int,
36
+ ) {
37
+ parcel.writeString(license)
38
+ parcel.writeString(baseUri)
39
+ parcel.writeString(assetBaseUri)
40
+ parcel.writeString(userId)
41
+ parcel.writeParcelable(source, flags)
42
+ }
43
+
44
+ override fun describeContents(): Int = 0
45
+
46
+ companion object CREATOR : Parcelable.Creator<EditorSettings> {
47
+ override fun createFromParcel(parcel: Parcel): EditorSettings = EditorSettings(parcel)
48
+
49
+ override fun newArray(size: Int): Array<EditorSettings?> = arrayOfNulls(size)
50
+
51
+ fun createFromMap(map: Map<String, Any>): EditorSettings? =
52
+ try {
53
+ val license = map["license"] as? String ?: throw (Exception("Missing value for key 'license.'"))
54
+ val sceneBaseUri = map["sceneBaseUri"] as? String ?: throw (Exception("Missing value for key 'sceneBaseUri.'"))
55
+ val assetBaseUri = map["assetBaseUri"] as? String
56
+ val userId = map["userId"] as? String
57
+ val source = (map["source"] as? Map<String, Any>)?.let { Source.createFromMap(it) }
58
+ EditorSettings(license, sceneBaseUri, assetBaseUri, userId, source)
59
+ } catch (e: Exception) {
60
+ null
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,39 @@
1
+ package ly.img.editor.reactnative.module.model
2
+
3
+ import android.os.Parcel
4
+ import android.os.Parcelable
5
+
6
+ /** The *EditorSourceType* defines the type of source that is loaded into the editor. */
7
+ enum class EditorSourceType(
8
+ val value: String,
9
+ ) : Parcelable {
10
+ /** A `.scene` file. */
11
+ SCENE("scene"),
12
+
13
+ /** An image file, e.g. `.png`. */
14
+ IMAGE("image"),
15
+
16
+ /** A video file, e.g. `.mp4`. */
17
+ VIDEO("video"),
18
+ ;
19
+
20
+ override fun writeToParcel(
21
+ parcel: Parcel,
22
+ flags: Int,
23
+ ) {
24
+ parcel.writeString(value)
25
+ }
26
+
27
+ override fun describeContents(): Int = 0
28
+
29
+ companion object CREATOR : Parcelable.Creator<EditorSourceType> {
30
+ override fun createFromParcel(parcel: Parcel): EditorSourceType {
31
+ val value = parcel.readString()
32
+ return entries.find { it.value == value } ?: throw IllegalArgumentException("Invalid value for EditorSourceType")
33
+ }
34
+
35
+ override fun newArray(size: Int): Array<EditorSourceType?> = arrayOfNulls(size)
36
+
37
+ fun fromValue(value: String): EditorSourceType? = entries.find { it.value == value }
38
+ }
39
+ }
@@ -0,0 +1,44 @@
1
+ package ly.img.editor.reactnative.module.model
2
+
3
+ import android.os.Parcel
4
+ import android.os.Parcelable
5
+
6
+ data class Source(
7
+ var source: String,
8
+ val type: EditorSourceType,
9
+ ) : Parcelable {
10
+ constructor(parcel: Parcel) : this(
11
+ parcel.readString() ?: throw (Exception("Missing value for key 'source'.")),
12
+ parcel.readParcelable(EditorSourceType::class.java.classLoader) ?: throw (Exception("Missing value for key 'type'.")),
13
+ )
14
+
15
+ override fun writeToParcel(
16
+ parcel: Parcel,
17
+ flags: Int,
18
+ ) {
19
+ parcel.writeString(source)
20
+ parcel.writeParcelable(type, flags)
21
+ }
22
+
23
+ override fun describeContents(): Int = 0
24
+
25
+ companion object CREATOR : Parcelable.Creator<Source> {
26
+ override fun createFromParcel(parcel: Parcel): Source = Source(parcel)
27
+
28
+ override fun newArray(size: Int): Array<Source?> = arrayOfNulls(size)
29
+
30
+ fun createFromMap(map: Map<String, Any>): Source? =
31
+ runCatching {
32
+ val source = map["source"] as? String
33
+ val typeRaw = map["type"] as? String
34
+
35
+ source?.let { s ->
36
+ typeRaw?.let { t ->
37
+ EditorSourceType.fromValue(t)?.let { type ->
38
+ Source(s, type)
39
+ }
40
+ }
41
+ }
42
+ }.getOrNull()
43
+ }
44
+ }
@@ -0,0 +1,7 @@
1
+ package ly.img.editor.reactnative.module
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext
4
+
5
+ abstract class IMGLYEditorModuleSpec internal constructor(
6
+ reactContext: ReactApplicationContext,
7
+ ) : NativeIMGLYEditorSpec(reactContext)
@@ -0,0 +1,18 @@
1
+ package ly.img.editor.reactnative.module
2
+
3
+ import com.facebook.react.bridge.Promise
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+ import com.facebook.react.bridge.ReadableMap
7
+
8
+ abstract class IMGLYEditorModuleSpec internal constructor(
9
+ context: ReactApplicationContext,
10
+ ) : ReactContextBaseJavaModule(context) {
11
+ abstract fun openEditor(
12
+ settings: ReadableMap?,
13
+ source: ReadableMap?,
14
+ preset: String?,
15
+ metadata: ReadableMap?,
16
+ promise: Promise?,
17
+ )
18
+ }
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './src/NativeIMGLYEditor';
2
+ export { default } from './src/NativeIMGLYEditor';
3
+
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './src/NativeIMGLYEditor';
2
+ export { default } from './src/NativeIMGLYEditor';
3
+
@@ -0,0 +1,13 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ @interface IMGLYConstants : NSObject
4
+
5
+ // Error codes
6
+ extern NSString* const IMGLYErrorExportFailed;
7
+ extern NSString* const IMGLYErrorParsing;
8
+
9
+ // Error messages
10
+ extern NSString* const IMGLYErrorExportFailedMessage;
11
+ extern NSString* const IMGLYErrorParsingMessage;
12
+
13
+ @end
@@ -0,0 +1,13 @@
1
+ #import "IMGLYConstants.h"
2
+
3
+ @implementation IMGLYConstants
4
+
5
+ // Error codes
6
+ NSString* const IMGLYErrorExportFailed = @"E_EXPORT_FAILED";
7
+ NSString* const IMGLYErrorParsing = @"E_PARSING";
8
+
9
+ // Error messages
10
+ NSString* const IMGLYErrorExportFailedMessage = @"The export of the asset failed due to: ";
11
+ NSString* const IMGLYErrorParsingMessage = @"Unable to parse the argument(s): ";
12
+
13
+ @end
@@ -0,0 +1,75 @@
1
+ import Foundation
2
+ import IMGLYEditor
3
+ import IMGLYEngine
4
+
5
+ /// An extension that provides convenience methods for `OnCreate` calls.
6
+ extension OnCreate {
7
+ /// Populates the editor based on a given `config`, `sourceType` and `defaultScene`.
8
+ /// - Parameters:
9
+ /// - settings: The `EditorSettings` containing the relevant editor information.
10
+ /// - sourceType: The `EditorSourceType` defining of which type the source is.
11
+ /// - defaultSource: The `URL` of the defaultScene if no `EditorSettings.source` is specified.
12
+ /// - Returns: An `OnCreate.Callback`.
13
+ @MainActor
14
+ public static func load(
15
+ _ settings: EditorSettings?,
16
+ _ sourceType: EditorSourceType = .scene,
17
+ defaultSource: URL? = nil
18
+ ) -> OnCreate.Callback {
19
+ { engine in
20
+ var url: URL?
21
+
22
+ if let source = settings?.source?.source {
23
+ if let sourceUrl = URL(string: source), sourceUrl.scheme != nil {
24
+ url = sourceUrl
25
+ } else {
26
+ throw "Specified URL is not valid."
27
+ }
28
+ } else if let defaultSource {
29
+ url = defaultSource
30
+ }
31
+
32
+ if let url {
33
+ switch settings?.source?.type ?? sourceType {
34
+ case .image:
35
+ try await engine.scene.create(fromImage: url)
36
+ let pages = try engine.scene.getPages()
37
+ let images = try engine.block.find(byType: .graphic)
38
+ guard let image = images.first, let page = pages.first else { throw "Scene could not be loaded." }
39
+ try engine.block.setFill(page, fill: engine.block.getFill(image))
40
+ try engine.block.destroy(image)
41
+ case .video:
42
+ try await engine.scene.create(fromVideo: url)
43
+ default:
44
+ try await engine.scene.load(from: url)
45
+ }
46
+ } else {
47
+ try await OnCreate.default(engine)
48
+ }
49
+
50
+ var assetUrl: URL?
51
+ if let assetBaseUri = settings?.assetBaseUri {
52
+ if let temp = URL(string: assetBaseUri), temp.scheme != nil {
53
+ assetUrl = temp
54
+ }
55
+ }
56
+ try await Self.loadDefaultAssets(baseURL: assetUrl)(engine)
57
+ }
58
+ }
59
+
60
+ /// Loads the default assets for the asset library.
61
+ /// - Parameter baseURL: The base `URL` from your CDN where the assets are hosted.
62
+ /// - Returns: A `Callback`.
63
+ static func loadDefaultAssets(baseURL: URL?) -> Callback {
64
+ { engine in
65
+ let baseURI = baseURL ?? Engine.assetBaseURL
66
+ try await engine.addDefaultAssetSources(baseURL: baseURI)
67
+ try await engine.addDemoAssetSources(
68
+ exclude: [.image, .video, .audio],
69
+ sceneMode: engine.scene.getMode(),
70
+ withUploadAssetSources: true
71
+ )
72
+ try await engine.asset.addSource(TextAssetSource(engine: engine))
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,64 @@
1
+ import Foundation
2
+ import IMGLYEditor
3
+ import IMGLYEngine
4
+
5
+ /// An extension that provides convenience methods for `OnExport` calls.
6
+ public extension OnExport {
7
+ /// Exports the asset to an `EditorResult`.
8
+ /// - Parameters:
9
+ /// - engine: The `Engine` that should be used.
10
+ /// - mimeType: The `MIMEType` of the exported artifact.
11
+ /// - thumbnailHeight: The height of the generated thumbnail.
12
+ /// - Returns: A populates `EditorResult`.
13
+ @MainActor
14
+ static func export(_ engine: Engine, _ mimeType: MIMEType,
15
+ _ thumbnailHeight: CGFloat = 100) async throws -> EditorResult {
16
+ let helper = ExportHelper()
17
+ let (data, type) = try await OnExport.export(engine, mimeType: mimeType)
18
+ let file = try helper.saveFile(data, type)
19
+ let scene = try await engine.scene.saveToString()
20
+ let localScene = try helper.saveScene(scene)
21
+
22
+ var thumbnail: URL?
23
+ if let firstPage = try engine.scene.getPages().first {
24
+ thumbnail = try await helper.saveThumbnail(firstPage, engine, thumbnailHeight)
25
+ }
26
+ return EditorResult(
27
+ scene: localScene?.absoluteString,
28
+ artifact: file.absoluteString,
29
+ thumbnail: thumbnail?.absoluteString
30
+ )
31
+ }
32
+
33
+ /// Exports a video to an `EditorResult`.
34
+ /// - Parameters:
35
+ /// - engine: The `Engine` that should be used.
36
+ /// - eventHandler: The `EditorEventHandler` used for event handling.
37
+ /// - mimeType: The `MIMEType` of the exported artifact.
38
+ /// - thumbnailHeight: The height of the generated thumbnail.
39
+ /// - Returns: A populates `EditorResult`.
40
+ @MainActor
41
+ static func exportVideo(
42
+ _ engine: Engine,
43
+ _ eventHandler: EditorEventHandler,
44
+ _ mimeType: MIMEType,
45
+ _ thumbnailHeight: CGFloat = 100
46
+ ) async throws -> EditorResult {
47
+ let helper = ExportHelper()
48
+ let (data, type) = try await OnExport.exportVideo(engine, eventHandler, mimeType: mimeType)
49
+ let file = try helper.saveFile(data, type)
50
+ let scene = try await engine.scene.saveToString()
51
+ let localScene = try helper.saveScene(scene)
52
+
53
+ var thumbnail: URL?
54
+ if let firstPage = try engine.scene.getPages().first {
55
+ thumbnail = try await helper.saveThumbnail(firstPage, engine, thumbnailHeight)
56
+ }
57
+ eventHandler.send(.exportCompleted {})
58
+ return EditorResult(
59
+ scene: localScene?.absoluteString,
60
+ artifact: file.absoluteString,
61
+ thumbnail: thumbnail?.absoluteString
62
+ )
63
+ }
64
+ }
@@ -0,0 +1,7 @@
1
+ import Foundation
2
+
3
+ /// An extension to allow throwing strings.
4
+ extension String: LocalizedError {
5
+ /// The error description.
6
+ public var errorDescription: String? { self }
7
+ }
@@ -0,0 +1,67 @@
1
+ import IMGLYEngine
2
+ import UIKit
3
+ import UniformTypeIdentifiers
4
+
5
+ /// A helper class for exporting.
6
+ @MainActor
7
+ class ExportHelper {
8
+ /// Saves given `data` to a file.
9
+ /// - Parameters:
10
+ /// - data: The data to save.
11
+ /// - contentType: The `UTType` of the file.
12
+ /// - Returns: The file `URL`.
13
+ func saveFile(_ data: Data, _ contentType: UTType) throws -> URL {
14
+ let fileName = "cesdk_export_artifact_" + UUID().uuidString
15
+ let url = FileManager.default.temporaryDirectory.appendingPathComponent(fileName, conformingTo: contentType)
16
+ try data.write(to: url, options: [.atomic])
17
+ return url
18
+ }
19
+
20
+ /// Saves given scene string to a file.
21
+ /// - Parameter scene: The scene as a `String`.
22
+ /// - Returns: The file `URL`.
23
+ func saveScene(_ scene: String) throws -> URL? {
24
+ let fileName = "cesdk_export_scene_" + UUID().uuidString
25
+ let url = FileManager.default.temporaryDirectory.appendingPathComponent(fileName, conformingTo: .plainText)
26
+ if let data = scene.data(using: .utf8) {
27
+ try data.write(to: url, options: [.atomic])
28
+ return url
29
+ }
30
+ return nil
31
+ }
32
+
33
+ // MARK: - Thumbnails
34
+
35
+ /// Generates and saves a thumbnail for a given block.
36
+ /// - Parameters:
37
+ /// - id: The `DesignBlockID` of the block.
38
+ /// - engine: The `Engine` to use.
39
+ /// - height: The height of the thumbnail.
40
+ /// - Returns: The `URL` to the thumbnail.
41
+ func saveThumbnail(_ id: DesignBlockID, _ engine: Engine, _ height: CGFloat) async throws -> URL {
42
+ let thumbnail = try await generateThumbnail(id, engine, height)
43
+ let fileName = "cesdk_export_thumbnail_" + UUID().uuidString
44
+ let url = FileManager.default.temporaryDirectory.appendingPathComponent(fileName, conformingTo: .png)
45
+ try thumbnail?.pngData()?.write(to: url, options: [.atomic])
46
+ return url
47
+ }
48
+
49
+ /// Generates a thumbnail for a given block.
50
+ /// - Parameters:
51
+ /// - id: The `DesignBlockID` of the block.
52
+ /// - engine: The `Engine` to use.
53
+ /// - height: The height of the thumbnail.
54
+ /// - Returns: The thumbnail as an `UIImage`.
55
+ private func generateThumbnail(_ id: DesignBlockID, _ engine: Engine, _ height: CGFloat) async throws -> UIImage? {
56
+ let stream = engine.block.generateVideoThumbnailSequence(
57
+ id,
58
+ thumbnailHeight: Int(height * UIScreen.main.scale),
59
+ timeRange: 0 ... 0.1,
60
+ numberOfFrames: 1
61
+ )
62
+ for try await thumbnail in stream {
63
+ return .init(cgImage: thumbnail.image, scale: UIScreen.main.scale, orientation: .up)
64
+ }
65
+ return nil
66
+ }
67
+ }
@@ -0,0 +1,3 @@
1
+ //
2
+ // Use this file to import your target's public headers that you would like to expose to Swift.
3
+ //
@@ -0,0 +1,11 @@
1
+ #ifdef RCT_NEW_ARCH_ENABLED
2
+ #import <IMGLYEditorModuleSpec/IMGLYEditorModuleSpec.h>
3
+
4
+ @interface IMGLYEditorModule : NSObject <NativeIMGLYEditorSpec>
5
+ #else
6
+ #import <React/RCTBridgeModule.h>
7
+
8
+ @interface IMGLYEditorModule : NSObject <RCTBridgeModule>
9
+ #endif
10
+
11
+ @end
@@ -0,0 +1,86 @@
1
+ #import "IMGLYEditorModule.h"
2
+ #import "IMGLYEditorModule/IMGLYEditorModule-Swift.h"
3
+ #import "IMGLYConstants.h"
4
+
5
+ @implementation IMGLYEditorModule
6
+
7
+ RCT_EXPORT_MODULE(IMGLYEditor)
8
+
9
+ #ifdef RCT_NEW_ARCH_ENABLED
10
+ - (void)openEditor:(JS::NativeIMGLYEditor::EditorSettings&)settings
11
+ source:(JS::NativeIMGLYEditor::Source&)source
12
+ preset:(NSString*)preset
13
+ metadata:(NSDictionary*)metadata
14
+ resolve:(RCTPromiseResolveBlock)resolve
15
+ reject:(RCTPromiseRejectBlock)reject {
16
+ NSMutableDictionary* mergedSettings = [@{
17
+ @"license" : settings.license(),
18
+ @"sceneBaseUri" : settings.sceneBaseUri(),
19
+ } mutableCopy];
20
+ if (settings.assetBaseUri() != nil) {
21
+ mergedSettings[@"assetBaseUri"] = settings.assetBaseUri();
22
+ }
23
+ if (settings.userId() != nil) {
24
+ mergedSettings[@"userId"] = settings.userId();
25
+ }
26
+
27
+ JS::NativeIMGLYEditor::Source* sourcePtr = &source;
28
+ if (sourcePtr != nullptr) {
29
+ mergedSettings[@"source"] = @{@"source" : source.source(), @"type" : source.type()};
30
+ }
31
+
32
+ [self open:mergedSettings preset:preset metadata:metadata resolve:resolve reject:reject];
33
+ }
34
+
35
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
36
+ (const facebook::react::ObjCTurboModule::InitParams&)params {
37
+ return std::make_shared<facebook::react::NativeIMGLYEditorSpecJSI>(params);
38
+ }
39
+ #else
40
+ RCT_EXPORT_METHOD(openEditor
41
+ : (nonnull NSDictionary*)settings source
42
+ : (nullable NSDictionary*)source preset
43
+ : (NSString*)preset metadata
44
+ : (NSDictionary*)metadata resolve
45
+ : (RCTPromiseResolveBlock)resolve reject
46
+ : (RCTPromiseRejectBlock)reject) {
47
+ NSMutableDictionary* mergedSettings = [settings mutableCopy];
48
+ mergedSettings[@"source"] = source;
49
+ [self open:mergedSettings preset:preset metadata:metadata resolve:resolve reject:reject];
50
+ }
51
+
52
+ #endif
53
+
54
+ - (void)open:(nonnull NSDictionary*)settings
55
+ preset:(NSString*)preset
56
+ metadata:(NSDictionary*)metadata
57
+ resolve:(RCTPromiseResolveBlock)resolve
58
+ reject:(RCTPromiseRejectBlock)reject {
59
+ EditorSettings* convertedSettings = [EditorSettings fromDictionary:settings];
60
+ if (convertedSettings == nil) {
61
+ reject(IMGLYErrorParsing, IMGLYErrorParsingMessage, nil);
62
+ return;
63
+ }
64
+ EditorPreset convertedPreset = [EditorPresetParser fromString:preset];
65
+ dispatch_async(dispatch_get_main_queue(), ^{
66
+ [[IMGLYEditorModuleSwiftAdapter shared] openEditor:convertedPreset
67
+ settings:convertedSettings
68
+ metadata:metadata
69
+ completion:^(EditorResult* _Nullable result, NSError* _Nullable error) {
70
+ if (result != nil) {
71
+ resolve(@ {
72
+ @"artifact" : result.artifact,
73
+ @"scene" : result.scene,
74
+ @"thumbnail" : result.thumbnail,
75
+ @"metadata" : result.metadata
76
+ });
77
+ } else if (error != nil) {
78
+ reject(IMGLYErrorExportFailed, IMGLYErrorExportFailedMessage, error);
79
+ } else {
80
+ resolve([NSNull null]);
81
+ }
82
+ }];
83
+ });
84
+ }
85
+
86
+ @end