@phucprime/react-native-image-editor 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,44 +1,32 @@
1
- <p align="center">
2
- <img src="https://lh3.googleusercontent.com/dsJXfHnUx0qvZIB_80F-q0iN18eIqmx6g10bmsVN8R6nEnLQDKvJ9lXCbnPCgDEZMw=s180" width="80" />
3
- </p>
4
-
5
1
  <h1 align="center">@phucprime/react-native-image-editor</h1>
6
2
 
7
3
  <p align="center">
8
- Native image editor for React Native — crop, draw, add text, stickers, and more.
4
+ Native image editor for React Native — crop, draw, text, stickers, and more.<br/>
5
+ Supports <strong>New Architecture</strong> (Fabric + TurboModules) and the classic bridge.
9
6
  </p>
10
7
 
11
8
  <p align="center">
12
9
  <a href="https://www.npmjs.com/package/@phucprime/react-native-image-editor"><img src="https://img.shields.io/npm/v/@phucprime/react-native-image-editor.svg?style=flat-square" alt="npm version" /></a>
13
10
  <a href="https://www.npmjs.com/package/@phucprime/react-native-image-editor"><img src="https://img.shields.io/npm/dm/@phucprime/react-native-image-editor.svg?style=flat-square" alt="npm downloads" /></a>
14
11
  <a href="https://github.com/phucprime/react-native-image-editor/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/@phucprime/react-native-image-editor.svg?style=flat-square" alt="license" /></a>
15
- <a href="https://github.com/phucprime/react-native-image-editor/pulls"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square" alt="PRs Welcome" /></a>
16
12
  </p>
17
13
 
18
14
  ---
19
15
 
20
- ## Features
21
-
22
- - **Cropping** — Resize and reframe images
23
- - **Drawing** — Freehand drawing with color picker
24
- - **Text** — Add text overlays with custom colors
25
- - **Stickers** — Add image stickers from your app resources
26
- - **Scaling & Rotating** — Transform any added element
27
- - **Saving & Sharing** — Export edited images
28
-
29
- ## Demo
30
-
31
16
  | iOS | Android |
32
17
  | :-: | :-: |
33
- | <img src="assets/ios.gif" width="300" /> | <img src="assets/android.gif" width="300" /> |
18
+ | <img src="assets/ios.gif" width="280" /> | <img src="assets/android.gif" width="280" /> |
19
+
20
+ ## Compatibility
34
21
 
35
- ## Requirements
22
+ | | Minimum | Tested |
23
+ | --- | --- | --- |
24
+ | React Native | 0.73 | **0.78.2** |
25
+ | iOS | 13.0 | 18 |
26
+ | Android | API 24 | API 35 |
27
+ | Architecture | Old Arch ✅ | **New Arch ✅** |
36
28
 
37
- | Platform | Minimum Version |
38
- | -------- | --------------- |
39
- | iOS | 13.0 |
40
- | Android | API 21 (5.0) |
41
- | React Native | >= 0.60 |
29
+ > Requires **JDK 17** and **Gradle 8.x** for Android builds.
42
30
 
43
31
  ## Installation
44
32
 
@@ -48,33 +36,18 @@ npm install @phucprime/react-native-image-editor
48
36
  yarn add @phucprime/react-native-image-editor
49
37
  ```
50
38
 
51
- ### iOS Setup
39
+ ### iOS
52
40
 
53
- 1. Add to your `ios/Podfile`:
41
+ Add to your `Podfile`:
54
42
 
55
43
  ```ruby
56
- use_native_modules!
57
-
58
- # Required: Flipper must be disabled when using iOSPhotoEditor with use_frameworks!
59
- flipper_config = FlipperConfiguration.disabled
60
-
61
44
  use_frameworks! :linkage => :static
62
45
  pod "iOSPhotoEditor", :git => "https://github.com/phucprime/photo-editor", :branch => "master"
63
-
64
- post_install do |installer|
65
- installer.pods_project.targets.each do |target|
66
- if target.name.include?('iOSPhotoEditor')
67
- target.build_configurations.each do |config|
68
- config.build_settings['SWIFT_VERSION'] = '5'
69
- end
70
- end
71
- end
72
- end
73
46
  ```
74
47
 
75
- 2. Run `pod install` in the `ios/` directory.
48
+ Then run `pod install`.
76
49
 
77
- 3. Add to `Info.plist`:
50
+ Add to `Info.plist`:
78
51
 
79
52
  ```xml
80
53
  <key>NSPhotoLibraryAddUsageDescription</key>
@@ -83,164 +56,70 @@ end
83
56
  <string>Allow access to select photos for editing</string>
84
57
  ```
85
58
 
86
- ### Android Setup
59
+ ### Android
87
60
 
88
- 1. Add JitPack repository to your root `android/build.gradle`:
61
+ Add JitPack to your root `android/build.gradle`:
89
62
 
90
63
  ```groovy
91
64
  allprojects {
92
65
  repositories {
93
- // ...existing repos...
94
66
  maven { url "https://jitpack.io" }
95
67
  }
96
68
  }
97
69
  ```
98
70
 
99
- 2. Add activities to `AndroidManifest.xml`:
71
+ Add activities to `AndroidManifest.xml`:
100
72
 
101
73
  ```xml
102
74
  <activity android:name="com.ahmedadeltito.photoeditor.PhotoEditorActivity" />
103
75
  <activity android:name="com.yalantis.ucrop.UCropActivity" />
104
76
  ```
105
77
 
106
- 3. For saving to external storage:
107
-
108
- ```xml
109
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
110
- ```
111
-
112
- 4. **JDK 17 required** (AGP 7.x is incompatible with JDK 21). Add to `android/gradle.properties`:
113
-
114
- ```properties
115
- org.gradle.java.home=/path/to/your/jdk-17
116
- ```
117
-
118
- > macOS Homebrew: `brew install openjdk@17`, then use `/opt/homebrew/Cellar/openjdk@17/<version>/libexec/openjdk.jdk/Contents/Home`
119
-
120
78
  ## Usage
121
79
 
122
- ### Promise-based (recommended)
80
+ ### Promise API (recommended)
123
81
 
124
82
  ```typescript
125
83
  import { ImageEditor } from '@phucprime/react-native-image-editor';
126
84
 
127
- try {
128
- const editedPath = await ImageEditor.edit('/path/to/image.jpg', {
129
- colors: ['#ff0000', '#00ff00', '#0000ff'],
130
- stickers: ['sticker1', 'sticker2'],
131
- });
132
- console.log('Edited image saved to:', editedPath);
133
- } catch (error) {
134
- console.log('Editor was cancelled');
135
- }
85
+ const editedPath = await ImageEditor.edit('/path/to/image.jpg', {
86
+ colors: ['#ff0000', '#00ff00', '#0000ff'],
87
+ stickers: ['sticker1', 'sticker2'],
88
+ });
136
89
  ```
137
90
 
138
- ### Callback-based
91
+ ### Callback API
139
92
 
140
93
  ```typescript
141
- import { ImageEditor } from '@phucprime/react-native-image-editor';
142
-
143
94
  ImageEditor.open({
144
95
  path: '/path/to/image.jpg',
145
- onDone: (editedPath) => {
146
- console.log('Edited image saved to:', editedPath);
147
- },
148
- onCancel: (resultCode) => {
149
- console.log('Editor cancelled with code:', resultCode);
150
- },
96
+ onDone: (path) => console.log('Saved:', path),
97
+ onCancel: () => console.log('Cancelled'),
151
98
  });
152
99
  ```
153
100
 
154
- > **Tip:** Use [react-native-fs](https://github.com/itinance/react-native-fs) to copy images into the app sandbox before editing.
155
-
156
- ## API Reference
157
-
158
- ### `ImageEditor.edit(path, options?): Promise<string>`
159
-
160
- Opens the editor and returns a promise with the edited image path.
101
+ ## Options
161
102
 
162
- | Parameter | Type | Description |
163
- | --------- | ---- | ----------- |
164
- | `path` | `string` | Path to the image file to edit |
165
- | `options` | `object` | Optional configuration (see below) |
103
+ | Property | Type | Description |
104
+ | -------- | ---- | ----------- |
105
+ | `path` | `string` | Path to the image file |
106
+ | `colors` | `string[]` | Hex colors for draw/text palette |
107
+ | `stickers` | `string[]` | Sticker image names from native resources |
108
+ | `hiddenControls` | `string[]` | Hide: `'text'` `'clear'` `'draw'` `'save'` `'share'` `'sticker'` `'crop'` |
109
+ | `languages` | `object` | UI localization strings |
166
110
 
167
- ### `ImageEditor.open(config): void`
168
-
169
- Opens the editor with callback-based API.
170
-
171
- ### Configuration
172
-
173
- | Property | Type | Default | Description |
174
- | -------- | ---- | ------- | ----------- |
175
- | `path` | `string` | *required* | Path to the image file |
176
- | `colors` | `string[]` | 13 default colors | Hex color strings for draw/text palette |
177
- | `stickers` | `string[]` | `[]` | Sticker image names from native resources |
178
- | `hiddenControls` | `EditorControl[]` | `[]` | Controls to hide: `'text'`, `'clear'`, `'draw'`, `'save'`, `'share'`, `'sticker'`, `'crop'` |
179
- | `languages` | `ImageEditorLanguage` | English defaults | UI localization strings |
180
- | `onDone` | `(path: string) => void` | — | Callback when editing completes |
181
- | `onCancel` | `(code: number) => void` | — | Callback when editing is cancelled |
111
+ > **Tip:** Use [react-native-fs](https://github.com/itinance/react-native-fs) to copy images into the app sandbox before editing.
182
112
 
183
113
  ## Stickers
184
114
 
185
- Add sticker images to your native project:
186
-
187
- - **iOS:** Add to the Resources folder
188
- - **Android:** Add to the `drawable` folder
189
-
190
- See the [Example](Example/) project for reference.
191
-
192
- ## Migrating from react-native-photo-editor
193
-
194
- ```diff
195
- - import PhotoEditor from 'react-native-photo-editor';
196
- + import { ImageEditor } from '@phucprime/react-native-image-editor';
197
-
198
- - PhotoEditor.Edit({ path: imagePath, onDone: handleDone });
199
- + ImageEditor.open({ path: imagePath, onDone: handleDone });
200
- // or use the Promise API:
201
- + const result = await ImageEditor.edit(imagePath);
202
- ```
203
-
204
- Update your iOS `Podfile`:
205
-
206
- ```diff
207
- - pod 'RNPhotoEditor', :path => '../node_modules/react-native-photo-editor'
208
- + pod 'RNImageEditor', :path => '../node_modules/@phucprime/react-native-image-editor'
209
- - pod 'iOSPhotoEditor', :git => 'https://github.com/prscX/photo-editor'
210
- + pod 'iOSPhotoEditor', :git => 'https://github.com/phucprime/photo-editor'
211
- ```
115
+ - **iOS** — Add images to the Resources folder
116
+ - **Android** — Add images to the `drawable` folder
212
117
 
213
- ## Troubleshooting
214
-
215
- ### React Native Firebase v6+
216
-
217
- If using Firebase with `use_frameworks!`, add to your `Podfile`:
218
-
219
- ```ruby
220
- $RNFirebaseAsStaticFramework = true
221
- ```
222
-
223
- ### Kotlin Metadata Version Mismatch (Gradle 8.x + RN 0.72)
224
-
225
- Add `-Xskip-metadata-version-check` to `node_modules/@react-native/gradle-plugin/build.gradle.kts`:
226
-
227
- ```kotlin
228
- tasks.withType<KotlinCompile> {
229
- kotlinOptions {
230
- freeCompilerArgs += listOf("-Xskip-metadata-version-check")
231
- }
232
- }
233
- ```
118
+ See the [Example](Example/) project for a working setup.
234
119
 
235
120
  ## Credits
236
121
 
237
- - Android Photo Editor: [eventtus/photo-editor-android](https://github.com/eventtus/photo-editor-android)
238
- - iOS Photo Editor: [eventtus/photo-editor](https://github.com/eventtus/photo-editor)
239
- - Original library: [prscX/react-native-photo-editor](https://github.com/prscX/react-native-photo-editor)
240
-
241
- ## Contributing
242
-
243
- See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
122
+ Based on [prscX/react-native-photo-editor](https://github.com/prscX/react-native-photo-editor), with photo editor SDKs by [eventtus](https://github.com/eventtus).
244
123
 
245
124
  ## License
246
125
 
@@ -4,6 +4,10 @@ def safeExtGet(prop, fallback) {
4
4
  rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5
5
  }
6
6
 
7
+ def isNewArchitectureEnabled() {
8
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
9
+ }
10
+
7
11
  buildscript {
8
12
  // The Android Gradle plugin is only required when opening the android folder stand-alone.
9
13
  // This avoids unnecessary downloads and potential conflicts when the library is included as a
@@ -16,19 +20,23 @@ buildscript {
16
20
  }
17
21
 
18
22
  dependencies {
19
- classpath('com.android.tools.build:gradle:7.4.2')
23
+ classpath('com.android.tools.build:gradle:8.2.1')
20
24
  }
21
25
  }
22
26
  }
23
27
 
28
+ if (isNewArchitectureEnabled()) {
29
+ apply plugin: 'com.facebook.react'
30
+ }
31
+
24
32
  android {
25
33
  namespace "ui.photoeditor"
26
- compileSdkVersion safeExtGet('compileSdkVersion', 33)
27
- buildToolsVersion safeExtGet('buildToolsVersion', '33.0.0')
34
+ compileSdkVersion safeExtGet('compileSdkVersion', 35)
35
+ buildToolsVersion safeExtGet('buildToolsVersion', '35.0.0')
28
36
 
29
37
  defaultConfig {
30
- minSdkVersion safeExtGet('minSdkVersion', 21)
31
- targetSdkVersion safeExtGet('targetSdkVersion', 33)
38
+ minSdkVersion safeExtGet('minSdkVersion', 24)
39
+ targetSdkVersion safeExtGet('targetSdkVersion', 35)
32
40
  versionCode 1
33
41
  versionName "1.0"
34
42
  }
@@ -38,8 +46,18 @@ android {
38
46
  }
39
47
 
40
48
  compileOptions {
41
- sourceCompatibility JavaVersion.VERSION_1_8
42
- targetCompatibility JavaVersion.VERSION_1_8
49
+ sourceCompatibility JavaVersion.VERSION_17
50
+ targetCompatibility JavaVersion.VERSION_17
51
+ }
52
+
53
+ sourceSets {
54
+ main {
55
+ if (isNewArchitectureEnabled()) {
56
+ java.srcDirs += ['src/newarch']
57
+ } else {
58
+ java.srcDirs += ['src/oldarch']
59
+ }
60
+ }
43
61
  }
44
62
  }
45
63
 
@@ -53,10 +71,9 @@ dependencies {
53
71
  implementation 'com.facebook.react:react-native:+'
54
72
  implementation files('libs/photo-editor-android.jar')
55
73
  implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1@aar'
56
- implementation 'com.nineoldandroids:library:2.4.0'
57
- implementation 'com.android.support:design:28.0.0'
58
- implementation 'com.android.support:appcompat-v7:28.0.0'
59
- implementation 'com.android.support.constraint:constraint-layout:1.0.2'
74
+ implementation 'com.google.android.material:material:1.9.0'
75
+ implementation 'androidx.appcompat:appcompat:1.6.1'
76
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
60
77
  implementation 'com.github.yalantis:ucrop:2.2.2-native'
61
- implementation 'com.android.support:exifinterface:27.1.1'
78
+ implementation 'androidx.exifinterface:exifinterface:1.3.7'
62
79
  }
@@ -25,7 +25,6 @@ import android.widget.ListView;
25
25
  import android.widget.ScrollView;
26
26
 
27
27
  import ui.photoeditor.R;
28
- import com.nineoldandroids.view.animation.AnimatorProxy;
29
28
 
30
29
  public class SlidingUpPanelLayout extends ViewGroup {
31
30
 
@@ -1150,11 +1149,7 @@ public class SlidingUpPanelLayout extends ViewGroup {
1150
1149
  private void applyParallaxForCurrentSlideOffset() {
1151
1150
  if (mParallaxOffset > 0) {
1152
1151
  int mainViewOffset = getCurrentParallaxOffset();
1153
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
1154
- mMainView.setTranslationY(mainViewOffset);
1155
- } else {
1156
- AnimatorProxy.wrap(mMainView).setTranslationY(mainViewOffset);
1157
- }
1152
+ mMainView.setTranslationY(mainViewOffset);
1158
1153
  }
1159
1154
  }
1160
1155
 
@@ -9,13 +9,12 @@ import com.facebook.react.bridge.ActivityEventListener;
9
9
  import com.facebook.react.bridge.BaseActivityEventListener;
10
10
  import com.facebook.react.bridge.Callback;
11
11
  import com.facebook.react.bridge.ReactApplicationContext;
12
- import com.facebook.react.bridge.ReactContextBaseJavaModule;
13
12
  import com.facebook.react.bridge.ReactMethod;
14
13
  import com.facebook.react.bridge.ReadableArray;
15
14
  import com.facebook.react.bridge.ReadableMap;
16
15
  import java.util.ArrayList;
17
16
 
18
- public class RNPhotoEditorModule extends ReactContextBaseJavaModule {
17
+ public class RNPhotoEditorModule extends RNPhotoEditorSpec {
19
18
 
20
19
  private static final int PHOTO_EDITOR_REQUEST = 1;
21
20
  private static final String E_PHOTO_EDITOR_CANCELLED = "E_PHOTO_EDITOR_CANCELLED";
@@ -9,7 +9,6 @@ import com.facebook.react.ReactPackage;
9
9
  import com.facebook.react.bridge.NativeModule;
10
10
  import com.facebook.react.bridge.ReactApplicationContext;
11
11
  import com.facebook.react.uimanager.ViewManager;
12
- import com.facebook.react.bridge.JavaScriptModule;
13
12
 
14
13
  public class RNPhotoEditorPackage implements ReactPackage {
15
14
  @Override
@@ -17,11 +16,6 @@ public class RNPhotoEditorPackage implements ReactPackage {
17
16
  return Arrays.<NativeModule>asList(new RNPhotoEditorModule(reactContext));
18
17
  }
19
18
 
20
- // Deprecated from RN 0.47
21
- public List<Class<? extends JavaScriptModule>> createJSModules() {
22
- return Collections.emptyList();
23
- }
24
-
25
19
  @Override
26
20
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
27
21
  return Collections.emptyList();
@@ -0,0 +1,14 @@
1
+ package ui.photoeditor;
2
+
3
+ import com.facebook.react.bridge.ReactApplicationContext;
4
+
5
+ /**
6
+ * New Architecture spec wrapper.
7
+ * On New Architecture, this extends the codegen-generated NativeRNPhotoEditorSpec
8
+ * which provides TurboModule integration with compile-time type validation.
9
+ */
10
+ abstract class RNPhotoEditorSpec extends NativeRNPhotoEditorSpec {
11
+ RNPhotoEditorSpec(ReactApplicationContext context) {
12
+ super(context);
13
+ }
14
+ }
@@ -0,0 +1,19 @@
1
+ package ui.photoeditor;
2
+
3
+ import com.facebook.react.bridge.Callback;
4
+ import com.facebook.react.bridge.ReactApplicationContext;
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
6
+ import com.facebook.react.bridge.ReadableMap;
7
+
8
+ /**
9
+ * Old Architecture compatibility spec.
10
+ * On Old Architecture (bridge mode), this extends ReactContextBaseJavaModule directly.
11
+ * The concrete module (RNPhotoEditorModule) extends this class.
12
+ */
13
+ abstract class RNPhotoEditorSpec extends ReactContextBaseJavaModule {
14
+ RNPhotoEditorSpec(ReactApplicationContext context) {
15
+ super(context);
16
+ }
17
+
18
+ public abstract void Edit(ReadableMap props, Callback onDone, Callback onCancel);
19
+ }
@@ -6,8 +6,3 @@
6
6
  #endif
7
7
 
8
8
  #import <UIKit/UIKit.h>
9
- @import iOSPhotoEditor;
10
-
11
- @interface RNPhotoEditor : NSObject <RCTBridgeModule, PhotoEditorDelegate>
12
-
13
- @end
@@ -1,163 +1,24 @@
1
+ #import <React/RCTBridgeModule.h>
1
2
 
2
- #import "RNPhotoEditor.h"
3
+ #ifdef RCT_NEW_ARCH_ENABLED
4
+ #import <RNPhotoEditorSpec/RNPhotoEditorSpec.h>
5
+ #endif
3
6
 
7
+ @interface RCT_EXTERN_MODULE (RNPhotoEditor, NSObject)
4
8
 
5
- @implementation RNPhotoEditor
9
+ RCT_EXTERN_METHOD(Edit : (NSDictionary *)props onDone : (RCTResponseSenderBlock)
10
+ onDone onCancel : (RCTResponseSenderBlock)onCancel)
6
11
 
7
- - (dispatch_queue_t)methodQueue
8
- {
9
- return dispatch_get_main_queue();
10
- }
11
- RCT_EXPORT_MODULE()
12
-
13
- NSString *_editImagePath = nil;
14
-
15
- RCTResponseSenderBlock _onDoneEditing = nil;
16
- RCTResponseSenderBlock _onCancelEditing = nil;
17
-
18
- - (void)doneEditingWithImage:(UIImage *)image {
19
- if (_onDoneEditing == nil) return;
20
-
21
- NSError* error;
22
-
23
- BOOL isPNG = [_editImagePath.pathExtension.lowercaseString isEqualToString:@"png"];
24
- NSString* path = _editImagePath;
25
-
26
- if ([path containsString:@"file://"]) {
27
- NSURL *url = [NSURL URLWithString:_editImagePath];
28
- path = url.path;
29
- }
30
-
31
- [isPNG ? UIImagePNGRepresentation(image) : UIImageJPEGRepresentation(image, 0.8) writeToFile:path options:NSDataWritingAtomic error:&error];
32
-
33
- if (error != nil)
34
- NSLog(@"write error %@", error);
35
-
36
- _onDoneEditing(@[path]);
37
- }
38
-
39
- - (void)canceledEditing {
40
- if (_onCancelEditing == nil) return;
41
-
42
- _onCancelEditing(@[]);
43
- }
44
-
45
- RCT_EXPORT_METHOD(Edit:(nonnull NSDictionary *)props onDone:(RCTResponseSenderBlock)onDone onCancel:(RCTResponseSenderBlock)onCancel) {
46
-
47
- dispatch_async(dispatch_get_main_queue(), ^{
48
- _editImagePath = [props objectForKey: @"path"];
49
-
50
- _onDoneEditing = onDone;
51
- _onCancelEditing = onCancel;
52
-
53
- PhotoEditorViewController *photoEditor = [[PhotoEditorViewController alloc] initWithNibName:@"PhotoEditorViewController" bundle: [NSBundle bundleForClass:[PhotoEditorViewController class]]];
54
- // set language
55
- NSDictionary *languages = [props objectForKey: @"languages"];
56
- [[TranslationService shared] initTranslations:languages];
57
-
58
- // Process Image for Editing
59
- UIImage *image = [UIImage imageWithContentsOfFile:_editImagePath];
60
- if (image == nil) {
61
- NSURL *url = [NSURL URLWithString:_editImagePath];
62
- NSData *data = [NSData dataWithContentsOfURL:url];
63
-
64
- image = [UIImage imageWithData:data];
65
- }
66
-
67
- photoEditor.image = image;
68
-
69
- // Process Stickers
70
- NSArray *stickers = [props objectForKey: @"stickers"];
71
- NSMutableArray *imageStickers = [[NSMutableArray alloc] initWithCapacity:stickers.count];
72
-
73
- for (NSString *sticker in stickers) {
74
- [imageStickers addObject: [UIImage imageNamed: sticker]];
75
- }
76
-
77
- photoEditor.stickers = imageStickers;
78
-
79
- //Process Controls
80
- NSArray *hiddenControls = [props objectForKey: @"hiddenControls"];
81
- NSMutableArray *passHiddenControls = [[NSMutableArray alloc] initWithCapacity:hiddenControls.count];
82
-
83
- for (NSString *hiddenControl in hiddenControls) {
84
- [passHiddenControls addObject: [[NSString alloc] initWithString: hiddenControl]];
85
- }
86
-
87
- photoEditor.hiddenControls = passHiddenControls;
88
-
89
- //Process Colors
90
- NSArray *colors = [props objectForKey: @"colors"];
91
- NSMutableArray *passColors = [[NSMutableArray alloc] initWithCapacity:colors.count];
92
-
93
- for (NSString *color in colors) {
94
- [passColors addObject: [self colorWithHexString: color]];
95
- }
96
-
97
- photoEditor.colors = passColors;
98
-
99
- // Invoke Editor
100
- photoEditor.photoEditorDelegate = self;
101
-
102
- // The default modal presenting is page sheet in ios 13, not full screen
103
- if (@available(iOS 13, *)) {
104
- [photoEditor setModalPresentationStyle: UIModalPresentationFullScreen];
105
- }
106
-
107
- id<UIApplicationDelegate> app = [[UIApplication sharedApplication] delegate];
108
- UINavigationController *rootViewController = ((UINavigationController*) app.window.rootViewController);
109
-
110
- if (rootViewController.presentedViewController) {
111
- [rootViewController.presentedViewController presentViewController:photoEditor animated:YES completion:nil];
112
- return;
113
- }
114
-
115
- [rootViewController presentViewController:photoEditor animated:YES completion:nil];
116
- });
117
- }
118
-
119
-
120
- - (CGFloat) colorComponentFrom: (NSString *) string start: (NSUInteger) start length: (NSUInteger) length {
121
- NSString *substring = [string substringWithRange: NSMakeRange(start, length)];
122
- NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat: @"%@%@", substring, substring];
123
- unsigned hexComponent;
124
- [[NSScanner scannerWithString: fullHex] scanHexInt: &hexComponent];
125
- return hexComponent / 255.0;
126
- }
127
-
128
- - (UIColor *) colorWithHexString: (NSString *) hexString {
129
- NSString *colorString = [[hexString stringByReplacingOccurrencesOfString: @"#" withString: @""] uppercaseString];
130
- CGFloat alpha, red, blue, green;
131
- switch ([colorString length]) {
132
- case 3: // #RGB
133
- alpha = 1.0f;
134
- red = [self colorComponentFrom: colorString start: 0 length: 1];
135
- green = [self colorComponentFrom: colorString start: 1 length: 1];
136
- blue = [self colorComponentFrom: colorString start: 2 length: 1];
137
- break;
138
- case 4: // #ARGB
139
- alpha = [self colorComponentFrom: colorString start: 0 length: 1];
140
- red = [self colorComponentFrom: colorString start: 1 length: 1];
141
- green = [self colorComponentFrom: colorString start: 2 length: 1];
142
- blue = [self colorComponentFrom: colorString start: 3 length: 1];
143
- break;
144
- case 6: // #RRGGBB
145
- alpha = 1.0f;
146
- red = [self colorComponentFrom: colorString start: 0 length: 2];
147
- green = [self colorComponentFrom: colorString start: 2 length: 2];
148
- blue = [self colorComponentFrom: colorString start: 4 length: 2];
149
- break;
150
- case 8: // #AARRGGBB
151
- alpha = [self colorComponentFrom: colorString start: 0 length: 2];
152
- red = [self colorComponentFrom: colorString start: 2 length: 2];
153
- green = [self colorComponentFrom: colorString start: 4 length: 2];
154
- blue = [self colorComponentFrom: colorString start: 6 length: 2];
155
- break;
156
- default:
157
- return nil;
158
- }
159
- return [UIColor colorWithRed: red green: green blue: blue alpha: alpha];
12
+ + (BOOL)requiresMainQueueSetup {
13
+ return YES;
160
14
  }
161
15
 
16
+ @end
162
17
 
18
+ #ifdef RCT_NEW_ARCH_ENABLED
19
+ // Conform to the codegen-generated protocol for TurboModule support.
20
+ // The actual implementation is in RNPhotoEditor.swift; this declaration
21
+ // tells the compiler the Swift class satisfies the protocol.
22
+ @interface RNPhotoEditor () <NativeRNPhotoEditorSpec>
163
23
  @end
24
+ #endif
@@ -0,0 +1,25 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ #ifdef RCT_NEW_ARCH_ENABLED
4
+ #import <RNPhotoEditorSpec/RNPhotoEditorSpec.h>
5
+ #endif
6
+
7
+ @interface RCT_EXTERN_MODULE(RNPhotoEditor, NSObject)
8
+
9
+ RCT_EXTERN_METHOD(Edit:(NSDictionary *)props
10
+ onDone:(RCTResponseSenderBlock)onDone
11
+ onCancel:(RCTResponseSenderBlock)onCancel)
12
+
13
+ + (BOOL)requiresMainQueueSetup {
14
+ return YES;
15
+ }
16
+
17
+ @end
18
+
19
+ #ifdef RCT_NEW_ARCH_ENABLED
20
+ // Conform to the codegen-generated protocol for TurboModule support.
21
+ // The actual implementation is in RNPhotoEditor.swift; this declaration
22
+ // tells the compiler the Swift class satisfies the protocol.
23
+ @interface RNPhotoEditor () <NativeRNPhotoEditorSpec>
24
+ @end
25
+ #endif
@@ -0,0 +1,168 @@
1
+ import Foundation
2
+ import UIKit
3
+ import iOSPhotoEditor
4
+
5
+ @objc(RNPhotoEditor)
6
+ class RNPhotoEditor: NSObject {
7
+
8
+ private var editImagePath: String?
9
+ private var onDoneEditing: RCTResponseSenderBlock?
10
+ private var onCancelEditing: RCTResponseSenderBlock?
11
+
12
+ @objc static func requiresMainQueueSetup() -> Bool {
13
+ return true
14
+ }
15
+
16
+ @objc func methodQueue() -> DispatchQueue {
17
+ return .main
18
+ }
19
+
20
+ @objc(Edit:onDone:onCancel:)
21
+ func edit(_ props: NSDictionary, onDone: @escaping RCTResponseSenderBlock, onCancel: @escaping RCTResponseSenderBlock) {
22
+ DispatchQueue.main.async { [weak self] in
23
+ guard let self = self else { return }
24
+
25
+ self.editImagePath = props["path"] as? String
26
+ self.onDoneEditing = onDone
27
+ self.onCancelEditing = onCancel
28
+
29
+ let photoEditor = PhotoEditorViewController(
30
+ nibName: "PhotoEditorViewController",
31
+ bundle: Bundle(for: PhotoEditorViewController.self)
32
+ )
33
+
34
+ // Set language translations
35
+ // TODO: Re-enable when TranslationService is added to the iOSPhotoEditor pod
36
+ // if let languages = props["languages"] as? [String: String] {
37
+ // TranslationService.shared.initTranslations(languages)
38
+ // }
39
+
40
+ // Process Image for Editing
41
+ var image: UIImage?
42
+ if let path = self.editImagePath {
43
+ image = UIImage(contentsOfFile: path)
44
+ if image == nil, let url = URL(string: path), let data = try? Data(contentsOf: url) {
45
+ image = UIImage(data: data)
46
+ }
47
+ }
48
+ photoEditor.image = image
49
+
50
+ // Process Stickers
51
+ if let stickers = props["stickers"] as? [String] {
52
+ photoEditor.stickers = stickers.compactMap { UIImage(named: $0) }
53
+ }
54
+
55
+ // Process Controls
56
+ if let hiddenControls = props["hiddenControls"] as? [String] {
57
+ photoEditor.hiddenControls = hiddenControls.compactMap { controlName in
58
+ switch controlName.lowercased() {
59
+ case "crop": return .crop
60
+ case "sticker": return .sticker
61
+ case "draw": return .draw
62
+ case "text": return .text
63
+ case "save": return .save
64
+ case "share": return .share
65
+ case "clear": return .clear
66
+ default: return nil
67
+ }
68
+ }
69
+ }
70
+
71
+ // Process Colors
72
+ if let colors = props["colors"] as? [String] {
73
+ photoEditor.colors = colors.compactMap { self.color(fromHexString: $0) }
74
+ }
75
+
76
+ // Invoke Editor
77
+ photoEditor.photoEditorDelegate = self
78
+
79
+ // The default modal presenting is page sheet in iOS 13, not full screen
80
+ photoEditor.modalPresentationStyle = .fullScreen
81
+
82
+ guard let rootViewController = UIApplication.shared.delegate?.window??.rootViewController else { return }
83
+
84
+ if let presentedVC = rootViewController.presentedViewController {
85
+ presentedVC.present(photoEditor, animated: true, completion: nil)
86
+ } else {
87
+ rootViewController.present(photoEditor, animated: true, completion: nil)
88
+ }
89
+ }
90
+ }
91
+
92
+ // MARK: - Hex Color Utilities
93
+
94
+ private func colorComponent(from string: String, start: Int, length: Int) -> CGFloat {
95
+ let startIndex = string.index(string.startIndex, offsetBy: start)
96
+ let endIndex = string.index(startIndex, offsetBy: length)
97
+ var substring = String(string[startIndex..<endIndex])
98
+ if length == 1 {
99
+ substring = "\(substring)\(substring)"
100
+ }
101
+ var hexComponent: UInt64 = 0
102
+ Scanner(string: substring).scanHexInt64(&hexComponent)
103
+ return CGFloat(hexComponent) / 255.0
104
+ }
105
+
106
+ private func color(fromHexString hexString: String) -> UIColor? {
107
+ let colorString = hexString.replacingOccurrences(of: "#", with: "").uppercased()
108
+ let alpha: CGFloat
109
+ let red: CGFloat
110
+ let green: CGFloat
111
+ let blue: CGFloat
112
+
113
+ switch colorString.count {
114
+ case 3: // #RGB
115
+ alpha = 1.0
116
+ red = colorComponent(from: colorString, start: 0, length: 1)
117
+ green = colorComponent(from: colorString, start: 1, length: 1)
118
+ blue = colorComponent(from: colorString, start: 2, length: 1)
119
+ case 4: // #ARGB
120
+ alpha = colorComponent(from: colorString, start: 0, length: 1)
121
+ red = colorComponent(from: colorString, start: 1, length: 1)
122
+ green = colorComponent(from: colorString, start: 2, length: 1)
123
+ blue = colorComponent(from: colorString, start: 3, length: 1)
124
+ case 6: // #RRGGBB
125
+ alpha = 1.0
126
+ red = colorComponent(from: colorString, start: 0, length: 2)
127
+ green = colorComponent(from: colorString, start: 2, length: 2)
128
+ blue = colorComponent(from: colorString, start: 4, length: 2)
129
+ case 8: // #AARRGGBB
130
+ alpha = colorComponent(from: colorString, start: 0, length: 2)
131
+ red = colorComponent(from: colorString, start: 2, length: 2)
132
+ green = colorComponent(from: colorString, start: 4, length: 2)
133
+ blue = colorComponent(from: colorString, start: 6, length: 2)
134
+ default:
135
+ return nil
136
+ }
137
+ return UIColor(red: red, green: green, blue: blue, alpha: alpha)
138
+ }
139
+ }
140
+
141
+ // MARK: - PhotoEditorDelegate
142
+
143
+ extension RNPhotoEditor: PhotoEditorDelegate {
144
+ func doneEditing(image: UIImage) {
145
+ guard let onDoneEditing = onDoneEditing, let editImagePath = editImagePath else { return }
146
+
147
+ let isPNG = (editImagePath as NSString).pathExtension.lowercased() == "png"
148
+ var path = editImagePath
149
+
150
+ if path.contains("file://"), let url = URL(string: editImagePath) {
151
+ path = url.path
152
+ }
153
+
154
+ let data = isPNG ? image.pngData() : image.jpegData(compressionQuality: 0.8)
155
+ do {
156
+ try data?.write(to: URL(fileURLWithPath: path), options: .atomic)
157
+ } catch {
158
+ NSLog("write error %@", error.localizedDescription)
159
+ }
160
+
161
+ onDoneEditing([path])
162
+ }
163
+
164
+ func canceledEditing() {
165
+ guard let onCancelEditing = onCancelEditing else { return }
166
+ onCancelEditing([])
167
+ }
168
+ }
@@ -0,0 +1,14 @@
1
+ import type { TurboModule } from 'react-native';
2
+ /**
3
+ * TurboModule codegen spec for RNPhotoEditor.
4
+ *
5
+ * This spec enables proper New Architecture (Fabric + TurboModules) support
6
+ * with compile-time type validation. On Old Architecture, TurboModuleRegistry
7
+ * falls back to the NativeModules bridge automatically.
8
+ */
9
+ export interface Spec extends TurboModule {
10
+ Edit(props: Object, onDone: (result: string) => void, onCancel: (result: number) => void): void;
11
+ }
12
+ declare const _default: Spec;
13
+ export default _default;
14
+ //# sourceMappingURL=NativeRNPhotoEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeRNPhotoEditor.d.ts","sourceRoot":"","sources":["../src/NativeRNPhotoEditor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;;;;;GAMG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,IAAI,CACF,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAChC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GACjC,IAAI,CAAC;CACT;;AAED,wBAAuE"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_native_1 = require("react-native");
4
+ exports.default = react_native_1.TurboModuleRegistry.getEnforcing('RNPhotoEditor');
5
+ //# sourceMappingURL=NativeRNPhotoEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeRNPhotoEditor.js","sourceRoot":"","sources":["../src/NativeRNPhotoEditor.ts"],"names":[],"mappings":";;AACA,+CAAmD;AAiBnD,kBAAe,kCAAmB,CAAC,YAAY,CAAO,eAAe,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kCAAkC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wDAAwD;AACxD,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,GACT,MAAM,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAEhC;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAuCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAM,WAAW;IACf;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAgC5C;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC,GAChE,OAAO,CAAC,MAAM,CAAC;IAYlB;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;CAG7C;AAED;;GAEG;AACH,QAAA,MAAM,WAAW,oBAAc,CAAC;AAEhC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AACpC,eAAe,WAAW,CAAC;AAG3B,mDAAmD;AACnD,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AACjD,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kCAAkC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wDAAwD;AACxD,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,GACT,MAAM,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAEhC;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAsCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,cAAM,WAAW;IACf;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAgC5C;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC,GAChE,OAAO,CAAC,MAAM,CAAC;IAYlB;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;CAG7C;AAED;;GAEG;AACH,QAAA,MAAM,WAAW,oBAAc,CAAC;AAEhC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AACpC,eAAe,WAAW,CAAC;AAG3B,mDAAmD;AACnD,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AACjD,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG,mBAAmB,CAAC"}
package/lib/index.js CHANGED
@@ -10,10 +10,16 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  }
11
11
  return t;
12
12
  };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
13
16
  Object.defineProperty(exports, "__esModule", { value: true });
14
17
  exports.PhotoEditor = exports.ImageEditor = void 0;
15
- const react_native_1 = require("react-native");
16
- const { RNPhotoEditor } = react_native_1.NativeModules;
18
+ const NativeRNPhotoEditor_1 = __importDefault(require("./NativeRNPhotoEditor"));
19
+ // TurboModuleRegistry.getEnforcing handles both architectures:
20
+ // - New Architecture: Uses TurboModule system directly
21
+ // - Old Architecture: Falls back to NativeModules bridge (RN 0.73+)
22
+ const RNPhotoEditor = NativeRNPhotoEditor_1.default;
17
23
  /** Default color palette for the editor. */
18
24
  const DEFAULT_COLORS = [
19
25
  '#000000',
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,+CAAuD;AAEvD,MAAM,EAAE,aAAa,EAAE,GAAG,4BAAa,CAAC;AA4FxC,4CAA4C;AAC5C,MAAM,cAAc,GAAa;IAC/B,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;CACV,CAAC;AAEF,oCAAoC;AACpC,MAAM,iBAAiB,GAAkC;IACvD,SAAS,EAAE,MAAM;IACjB,SAAS,EAAE,MAAM;IACjB,aAAa,EAAE,WAAW;IAC1B,WAAW,EAAE,QAAQ;IACrB,YAAY,EAAE,SAAS;IACvB,iBAAiB,EAAE,cAAc;IACjC,iBAAiB,EAAE,gBAAgB;IACnC,mBAAmB,EAAE,4BAA4B;IACjD,yBAAyB,EACvB,0DAA0D;IAC5D,WAAW,EAAE,UAAU;IACvB,MAAM,EAAE,SAAS;IACjB,oBAAoB,EAClB,qDAAqD;IACvD,gBAAgB,EAAE,aAAa;IAC/B,WAAW,EAAE,QAAQ;CACtB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW;IACf;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,MAAyB;QACnC,MAAM,EACJ,QAAQ,GAAG,EAAE,EACb,cAAc,GAAG,EAAE,EACnB,MAAM,GAAG,cAAc,EACvB,SAAS,EACT,MAAM,EACN,QAAQ,KAEN,MAAM,EADL,IAAI,UACL,MAAM,EARJ,2EAQL,CAAS,CAAC;QAEX,MAAM,eAAe,GAAG,SAAS;YAC/B,CAAC,iCAAM,iBAAiB,GAAK,SAAS,EACtC,CAAC,CAAC,iBAAiB,CAAC;QAEtB,aAAa,CAAC,IAAI,iBAEd,MAAM;YACN,cAAc;YACd,QAAQ,EACR,SAAS,EAAE,eAAe,IACvB,IAAI,GAET,CAAC,SAAiB,EAAE,EAAE;YACpB,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAG,SAAS,CAAC,CAAC;QACtB,CAAC,EACD,CAAC,UAAkB,EAAE,EAAE;YACrB,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAG,UAAU,CAAC,CAAC;QACzB,CAAC,CACF,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CACT,IAAY,EACZ,OAAiE;QAEjE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,WAAW,CAAC,IAAI,iCACX,OAAO,KACV,IAAI,EACJ,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,CAAC,UAAU,EAAE,EAAE,CACvB,MAAM,CAAC,IAAI,KAAK,CAAC,+BAA+B,UAAU,EAAE,CAAC,CAAC,IAChE,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,MAAyB;QACnC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;CACF;AAOQ,kCAAW;AALpB;;GAEG;AACH,MAAM,WAAW,GAAG,WAAW,CAAC;AAEV,kCAAW;AACjC,kBAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,gFAAwD;AAExD,+DAA+D;AAC/D,uDAAuD;AACvD,oEAAoE;AACpE,MAAM,aAAa,GAAG,6BAAmB,CAAC;AA4F1C,4CAA4C;AAC5C,MAAM,cAAc,GAAa;IAC/B,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;CACV,CAAC;AAEF,oCAAoC;AACpC,MAAM,iBAAiB,GAAkC;IACvD,SAAS,EAAE,MAAM;IACjB,SAAS,EAAE,MAAM;IACjB,aAAa,EAAE,WAAW;IAC1B,WAAW,EAAE,QAAQ;IACrB,YAAY,EAAE,SAAS;IACvB,iBAAiB,EAAE,cAAc;IACjC,iBAAiB,EAAE,gBAAgB;IACnC,mBAAmB,EAAE,4BAA4B;IACjD,yBAAyB,EACvB,0DAA0D;IAC5D,WAAW,EAAE,UAAU;IACvB,MAAM,EAAE,SAAS;IACjB,oBAAoB,EAAE,qDAAqD;IAC3E,gBAAgB,EAAE,aAAa;IAC/B,WAAW,EAAE,QAAQ;CACtB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW;IACf;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,MAAyB;QACnC,MAAM,EACJ,QAAQ,GAAG,EAAE,EACb,cAAc,GAAG,EAAE,EACnB,MAAM,GAAG,cAAc,EACvB,SAAS,EACT,MAAM,EACN,QAAQ,KAEN,MAAM,EADL,IAAI,UACL,MAAM,EARJ,2EAQL,CAAS,CAAC;QAEX,MAAM,eAAe,GAAG,SAAS;YAC/B,CAAC,iCAAM,iBAAiB,GAAK,SAAS,EACtC,CAAC,CAAC,iBAAiB,CAAC;QAEtB,aAAa,CAAC,IAAI,iBAEd,MAAM;YACN,cAAc;YACd,QAAQ,EACR,SAAS,EAAE,eAAe,IACvB,IAAI,GAET,CAAC,SAAiB,EAAE,EAAE;YACpB,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAG,SAAS,CAAC,CAAC;QACtB,CAAC,EACD,CAAC,UAAkB,EAAE,EAAE;YACrB,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAG,UAAU,CAAC,CAAC;QACzB,CAAC,CACF,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CACT,IAAY,EACZ,OAAiE;QAEjE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,WAAW,CAAC,IAAI,iCACX,OAAO,KACV,IAAI,EACJ,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,CAAC,UAAU,EAAE,EAAE,CACvB,MAAM,CAAC,IAAI,KAAK,CAAC,+BAA+B,UAAU,EAAE,CAAC,CAAC,IAChE,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,MAAyB;QACnC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;CACF;AAOQ,kCAAW;AALpB;;GAEG;AACH,MAAM,WAAW,GAAG,WAAW,CAAC;AAEV,kCAAW;AACjC,kBAAe,WAAW,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phucprime/react-native-image-editor",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "React Native: Native Image Editor for iOS and Android with drawing, text, stickers, cropping and more",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -31,7 +31,7 @@
31
31
  "format": "prettier --write src/",
32
32
  "clean": "rm -rf lib/",
33
33
  "prepare": "npm run clean && npm run build",
34
- "release": "npm run prepare && npm publish --access public"
34
+ "release": "npm run prepare && npm publish --access public --registry https://registry.npmjs.org/"
35
35
  },
36
36
  "keywords": [
37
37
  "react-native",
@@ -49,9 +49,17 @@
49
49
  ],
50
50
  "author": "phucprime",
51
51
  "license": "Apache-2.0",
52
+ "codegenConfig": {
53
+ "name": "RNPhotoEditorSpec",
54
+ "type": "modules",
55
+ "jsSrcsDir": "src",
56
+ "android": {
57
+ "javaPackageName": "ui.photoeditor"
58
+ }
59
+ },
52
60
  "peerDependencies": {
53
- "react": ">=16.8.0",
54
- "react-native": ">=0.60.0"
61
+ "react": ">=18.2.0",
62
+ "react-native": ">=0.73.0"
55
63
  },
56
64
  "peerDependenciesMeta": {
57
65
  "react": {
@@ -62,12 +70,12 @@
62
70
  }
63
71
  },
64
72
  "devDependencies": {
65
- "@types/react": "^18.2.0",
66
- "@types/react-native": "^0.72.0",
73
+ "@types/react": "^18.2.0 || ^19.0.0",
67
74
  "prettier": "^3.2.0",
75
+ "react-native": "0.78.2",
68
76
  "typescript": "^5.3.0"
69
77
  },
70
78
  "engines": {
71
79
  "node": ">=16.0.0"
72
80
  }
73
- }
81
+ }
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
12
12
  s.platforms = { :ios => "13.0" }
13
13
  s.source = { :git => "https://github.com/phucprime/react-native-image-editor.git", :tag => "#{s.version}" }
14
14
 
15
- s.source_files = "ios/**/*.{h,m,mm}"
15
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
16
16
  s.dependency "iOSPhotoEditor"
17
17
 
18
18
  # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
@@ -0,0 +1,19 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+
4
+ /**
5
+ * TurboModule codegen spec for RNPhotoEditor.
6
+ *
7
+ * This spec enables proper New Architecture (Fabric + TurboModules) support
8
+ * with compile-time type validation. On Old Architecture, TurboModuleRegistry
9
+ * falls back to the NativeModules bridge automatically.
10
+ */
11
+ export interface Spec extends TurboModule {
12
+ Edit(
13
+ props: Object,
14
+ onDone: (result: string) => void,
15
+ onCancel: (result: number) => void,
16
+ ): void;
17
+ }
18
+
19
+ export default TurboModuleRegistry.getEnforcing<Spec>('RNPhotoEditor');
package/src/index.ts CHANGED
@@ -1,6 +1,9 @@
1
- import { NativeModules, Platform } from 'react-native';
1
+ import NativeRNPhotoEditor from './NativeRNPhotoEditor';
2
2
 
3
- const { RNPhotoEditor } = NativeModules;
3
+ // TurboModuleRegistry.getEnforcing handles both architectures:
4
+ // - New Architecture: Uses TurboModule system directly
5
+ // - Old Architecture: Falls back to NativeModules bridge (RN 0.73+)
6
+ const RNPhotoEditor = NativeRNPhotoEditor;
4
7
 
5
8
  /**
6
9
  * Localization strings for the image editor UI.
@@ -123,8 +126,7 @@ const DEFAULT_LANGUAGES: Required<ImageEditorLanguage> = {
123
126
  'To attach photos, we need to access media on your device',
124
127
  continueTxt: 'Continue',
125
128
  notNow: 'NOT NOW',
126
- mediaAccessDeniedMsg:
127
- 'You denied storage access, no photos will be added.',
129
+ mediaAccessDeniedMsg: 'You denied storage access, no photos will be added.',
128
130
  saveImageSucceed: 'Image saved',
129
131
  eraserTitle: 'Eraser',
130
132
  };