react-native-photo-video-editor 0.1.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.
- package/CHANGELOG.md +27 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +20 -0
- package/PhotoVideoEditor.podspec +23 -0
- package/README.md +53 -0
- package/android/build.gradle +61 -0
- package/android/src/main/AndroidManifest.xml +12 -0
- package/android/src/main/java/com/photovideoeditor/PhotoVideoEditorModule.kt +126 -0
- package/android/src/main/java/com/photovideoeditor/PhotoVideoEditorPackage.kt +31 -0
- package/android/src/main/java/com/photovideoeditor/editor/PhotoVideoEditorActivity.kt +2521 -0
- package/android/src/main/java/com/photovideoeditor/editor/README.md +5 -0
- package/android/src/main/java/com/photovideoeditor/files/SourceResolver.kt +29 -0
- package/android/src/main/java/com/photovideoeditor/media/BitmapSampling.kt +11 -0
- package/android/src/main/java/com/photovideoeditor/model/EditorRequest.kt +3 -0
- package/android/src/main/java/com/photovideoeditor/photo/export/PhotoExporter.kt +201 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/OverlayGeometry.kt +22 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoAdjustmentRenderer.kt +160 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoAdjustments.kt +55 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoEditSession.kt +121 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoFilterPresets.kt +64 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayer.kt +54 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayerRenderer.kt +108 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayerStack.kt +60 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformState.kt +52 -0
- package/android/src/main/java/com/photovideoeditor/photo/ui/CropOverlayView.kt +168 -0
- package/android/src/main/java/com/photovideoeditor/photo/ui/FilterThumbnailLoader.kt +57 -0
- package/android/src/main/java/com/photovideoeditor/photo/ui/LayerOverlayView.kt +202 -0
- package/android/src/main/java/com/photovideoeditor/photo/ui/PhotoPreviewRenderCoordinator.kt +57 -0
- package/android/src/main/java/com/photovideoeditor/photo/ui/ZoomableImageView.kt +96 -0
- package/android/src/main/java/com/photovideoeditor/stickers/OnlineStickerSheet.kt +310 -0
- package/android/src/main/java/com/photovideoeditor/stickers/OpenMojiCatalog.kt +172 -0
- package/android/src/main/java/com/photovideoeditor/stickers/RemoteImageLoader.kt +110 -0
- package/android/src/main/java/com/photovideoeditor/ui/DesignTokens.kt +93 -0
- package/android/src/main/java/com/photovideoeditor/ui/components/EditorComponents.kt +119 -0
- package/android/src/main/java/com/photovideoeditor/validation/EditorRequestValidator.kt +11 -0
- package/android/src/main/java/com/photovideoeditor/video/cache/TimelineThumbnailRepository.kt +130 -0
- package/android/src/main/java/com/photovideoeditor/video/export/VideoExporter.kt +319 -0
- package/android/src/main/java/com/photovideoeditor/video/render/VideoClip.kt +20 -0
- package/android/src/main/java/com/photovideoeditor/video/render/VideoEditSession.kt +136 -0
- package/android/src/main/java/com/photovideoeditor/video/render/VideoThumbnailGenerator.kt +31 -0
- package/android/src/main/java/com/photovideoeditor/video/render/VideoTransformState.kt +48 -0
- package/android/src/main/java/com/photovideoeditor/video/ui/TrimRangeView.kt +104 -0
- package/android/src/main/res/drawable/ic_add.xml +9 -0
- package/android/src/main/res/drawable/ic_arrow_back.xml +9 -0
- package/android/src/main/res/drawable/ic_aspect_ratio.xml +9 -0
- package/android/src/main/res/drawable/ic_auto_fix_high.xml +9 -0
- package/android/src/main/res/drawable/ic_check.xml +9 -0
- package/android/src/main/res/drawable/ic_chevron_left.xml +9 -0
- package/android/src/main/res/drawable/ic_chevron_right.xml +9 -0
- package/android/src/main/res/drawable/ic_close.xml +9 -0
- package/android/src/main/res/drawable/ic_compare.xml +9 -0
- package/android/src/main/res/drawable/ic_content_copy.xml +9 -0
- package/android/src/main/res/drawable/ic_content_cut.xml +9 -0
- package/android/src/main/res/drawable/ic_crop.xml +9 -0
- package/android/src/main/res/drawable/ic_crop_square.xml +9 -0
- package/android/src/main/res/drawable/ic_delete.xml +9 -0
- package/android/src/main/res/drawable/ic_drag_indicator.xml +9 -0
- package/android/src/main/res/drawable/ic_face.xml +9 -0
- package/android/src/main/res/drawable/ic_filter_center_focus.xml +9 -0
- package/android/src/main/res/drawable/ic_flare.xml +9 -0
- package/android/src/main/res/drawable/ic_format_size.xml +9 -0
- package/android/src/main/res/drawable/ic_fullscreen.xml +9 -0
- package/android/src/main/res/drawable/ic_layers.xml +9 -0
- package/android/src/main/res/drawable/ic_light_mode.xml +9 -0
- package/android/src/main/res/drawable/ic_lock.xml +9 -0
- package/android/src/main/res/drawable/ic_magic_button.xml +9 -0
- package/android/src/main/res/drawable/ic_movie.xml +9 -0
- package/android/src/main/res/drawable/ic_opacity.xml +9 -0
- package/android/src/main/res/drawable/ic_palette.xml +9 -0
- package/android/src/main/res/drawable/ic_photo_filter.xml +9 -0
- package/android/src/main/res/drawable/ic_redo.xml +9 -0
- package/android/src/main/res/drawable/ic_rotate_90_degrees_ccw.xml +9 -0
- package/android/src/main/res/drawable/ic_rotate_left.xml +9 -0
- package/android/src/main/res/drawable/ic_rotate_right.xml +9 -0
- package/android/src/main/res/drawable/ic_sentiment_satisfied.xml +9 -0
- package/android/src/main/res/drawable/ic_share.xml +9 -0
- package/android/src/main/res/drawable/ic_speed.xml +9 -0
- package/android/src/main/res/drawable/ic_straighten.xml +9 -0
- package/android/src/main/res/drawable/ic_title.xml +9 -0
- package/android/src/main/res/drawable/ic_tune.xml +9 -0
- package/android/src/main/res/drawable/ic_undo.xml +9 -0
- package/android/src/main/res/drawable/ic_visibility_off.xml +9 -0
- package/android/src/main/res/drawable/ic_wallpaper.xml +9 -0
- package/android/src/main/res/values/styles.xml +10 -0
- package/docs/api.md +9 -0
- package/docs/architecture.md +9 -0
- package/docs/customization.md +18 -0
- package/docs/development.md +21 -0
- package/docs/formats.md +27 -0
- package/docs/performance.md +3 -0
- package/docs/permissions.md +3 -0
- package/docs/photo-editor.md +41 -0
- package/docs/roadmap.md +12 -0
- package/docs/technology-decisions.md +17 -0
- package/docs/video-editor.md +76 -0
- package/ios/Editor/DesignTokens.swift +75 -0
- package/ios/Editor/PhotoVideoEditorOutcome.swift +9 -0
- package/ios/Editor/PhotoVideoEditorViewController.swift +2064 -0
- package/ios/Editor/README.md +5 -0
- package/ios/Editor/UI/EditorComponents.swift +138 -0
- package/ios/Files/SourceResolver.swift +13 -0
- package/ios/Models/EditorRequest.swift +3 -0
- package/ios/Photo/Export/PhotoExporter.swift +144 -0
- package/ios/Photo/Rendering/OverlayGeometry.swift +22 -0
- package/ios/Photo/Rendering/PhotoAdjustmentRenderer.swift +141 -0
- package/ios/Photo/Rendering/PhotoAdjustments.swift +57 -0
- package/ios/Photo/Rendering/PhotoEditSession.swift +121 -0
- package/ios/Photo/Rendering/PhotoFilterPresets.swift +45 -0
- package/ios/Photo/Rendering/PhotoLayer.swift +59 -0
- package/ios/Photo/Rendering/PhotoLayerRenderer.swift +94 -0
- package/ios/Photo/Rendering/PhotoLayerStack.swift +60 -0
- package/ios/Photo/Rendering/PhotoTransformState.swift +49 -0
- package/ios/Photo/Rendering/PixelBuffer.swift +68 -0
- package/ios/Photo/UI/CropOverlayView.swift +161 -0
- package/ios/Photo/UI/LayerOverlayView.swift +176 -0
- package/ios/Photo/UI/ZoomableImageView.swift +79 -0
- package/ios/PhotoVideoEditor.mm +20 -0
- package/ios/PhotoVideoEditor.swift +70 -0
- package/ios/Stickers/OnlineStickerSheetViewController.swift +418 -0
- package/ios/Stickers/OpenMojiCatalog.swift +188 -0
- package/ios/Stickers/RemoteImageLoader.swift +127 -0
- package/ios/Validation/EditorRequestValidator.swift +9 -0
- package/ios/Video/Cache/TimelineThumbnailRepository.swift +148 -0
- package/ios/Video/Export/VideoExporter.swift +275 -0
- package/ios/Video/Render/VideoClip.swift +21 -0
- package/ios/Video/Render/VideoEditSession.swift +99 -0
- package/ios/Video/Render/VideoThumbnailGenerator.swift +25 -0
- package/ios/Video/Render/VideoTransformState.swift +23 -0
- package/ios/Video/UI/TrimRangeView.swift +91 -0
- package/lib/module/NativePhotoVideoEditor.js +5 -0
- package/lib/module/NativePhotoVideoEditor.js.map +1 -0
- package/lib/module/errors.js +29 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/events.js +27 -0
- package/lib/module/events.js.map +1 -0
- package/lib/module/index.js +91 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/project.js +4 -0
- package/lib/module/project.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativePhotoVideoEditor.d.ts +11 -0
- package/lib/typescript/src/NativePhotoVideoEditor.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +11 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/events.d.ts +5 -0
- package/lib/typescript/src/events.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +15 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/project.d.ts +24 -0
- package/lib/typescript/src/project.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +108 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +154 -0
- package/src/NativePhotoVideoEditor.ts +12 -0
- package/src/errors.ts +78 -0
- package/src/events.ts +43 -0
- package/src/index.ts +123 -0
- package/src/project.ts +17 -0
- package/src/types.ts +121 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Adopt the "Studio Violet" design system exported from Stitch (`stitch_react_native_photo_editor_sdk/`) for the photo editor shell and every existing tool: new color/spacing/radius tokens (`DesignTokens.kt`/`DesignTokens.swift`), a real Material Symbols icon set (46 icons fetched from source SVGs and converted to Android vector drawables; SF Symbols on iOS), an icon-tile main tool rail (expanded to the full 16-tool set from the mockup: Crop/Adjust/Filters/Effects/Blur/Text/Stickers/Shapes/Draw/Frames/Overlays/Background/Retouch/Remove/Layers/Resize), a circular-close/pill-Export header, Rotate/Flip moved into the Crop sub-bar, Adjust and Filters split into separate tools (previously combined), real per-preset thumbnail cards for Filters, a card grid for Stickers/Shapes, color-swatch controls for Draw, icon actions for the Layer property bar, and Color/Size properties for text layers. Android is Gradle-verified end to end (`compileDebugKotlin`, `assembleDebug` for the library and example app); iOS mirrors the header, tool rail, and Crop sub-bar but is unbuilt (no macOS/Xcode here) — the remaining tool sub-bars (Filters cards, Stickers/Shapes grid, Draw, Layers, Text) are visually unchanged on iOS for now. Toolbar entries with no mockup-independent functionality yet (Effects, Frames, Overlays, Background, Retouch) still show "coming in a later milestone".
|
|
6
|
+
- Add two new photo tools shown in the mockups but not previously implemented: **Blur & Depth** (synthetic Radial/Linear tilt-shift blur via `RadialGradient`/`LinearGradient` + `PorterDuff.DST_IN` alpha-mask compositing — no ML/device depth sensing, since that's unverifiable without a device, so the mockup's ML-driven "Portrait" mode is intentionally out of scope) and **Canvas Resize & Social Presets** (IG Post/Story/Square, YouTube, Twitter presets, reusing the existing crop-aspect-ratio mechanism plus an exact export pixel-size override). Both are Android-only so far, Gradle-verified, wired into both preview and export. AI Object Removal / Magic Eraser was explicitly excluded from this pass per instruction.
|
|
7
|
+
- Add focused photo/video editor APIs, native full-screen preview shells, feature-aware toolbars, typed editor events, and the versioned project model foundation.
|
|
8
|
+
- Expand the example into separate pick and launch actions.
|
|
9
|
+
- Apply `theme` (background/toolbar/text/primary colors, status bar style) in the Android and iOS native editor shells; previously accepted but ignored.
|
|
10
|
+
- Fix an Android Kotlin build failure in the toolbar layout (`orientation = HORIZONTAL` did not resolve; now `LinearLayout.HORIZONTAL`).
|
|
11
|
+
- Fix the Android header and toolbar rendering underneath the status/gesture bars on targetSdk 35+ (enforced edge-to-edge draws the window behind system bars by default). The header/toolbar now consume `WindowInsetsCompat` system bar insets and grow by that amount instead of being obscured.
|
|
12
|
+
- Add real photo editing (Milestone 2): freeform + aspect-ratio-preset crop, 90° rotate, horizontal/vertical flip, fine straighten, pan/pinch-to-zoom preview, and JPEG/PNG/WebP export with EXIF orientation handling and optional metadata preservation. `EditorResult` now returns real `width`/`height`/`fileSize`/`mimeType` for photo edits. Filters/Text/Stickers/Draw remain stubs (Milestone 3/4) but now show a "coming in a later milestone" message instead of doing nothing.
|
|
13
|
+
- Add photo adjustments and filters (Milestone 3): Brightness, Contrast, Saturation, Exposure, Gamma, Temperature, Tint, Highlights, Shadows, Sharpen, Blur, Pixelate, Mirror, and five original preset filters (Mono, Noir, Fade, Chrome, Warmth) with an adjustable blend strength. Applied identically in preview and export on both platforms via matching hand-implemented formulas (no CoreImage/RenderScript dependency). Text/Stickers/Draw remain stubs (Milestone 4).
|
|
14
|
+
- Add photo layers (Milestone 4): text, stickers (8 built-in + consumer `stickerAssets`), shapes (rect/oval/line), and freehand drawing, with tap-to-select, drag-to-move, and Scale/Rotate/Opacity sliders per layer. Duplicate/Lock/Hide/Front/Back/Delete actions, plus header Undo/Redo backed by a 50-entry snapshot history (one entry per committed operation, never per drag frame). Layers are burned in identically at preview and export resolution. Added `EditorOptions.stickerAssets` and `EditorOptions.fonts` to the public TypeScript API; `fonts` is accepted and forwarded but not yet wired to load custom font files (documented gap, see `docs/photo-editor.md`).
|
|
15
|
+
- Fix Android tool sub-bars (crop/filters/stickers/draw/layers/video-aspect) rendering under the gesture nav bar when first shown. Only the main toolbar had edge-to-edge bottom-inset handling; every sub-bar that can replace it at the bottom of the screen now gets the same treatment, applied up front to all of them (Android dispatches insets regardless of visibility, so whichever bar becomes visible when a tool is selected is already correctly padded).
|
|
16
|
+
- Add real video editing (Milestone 5): custom player with play/pause/scrub, a dual-handle trim range bar backed by a background-generated thumbnail strip, mute, cover-frame timestamp selection, 90° rotate, horizontal flip, and H.264/AAC MP4 export via Media3 `Transformer` (Android) / `AVAssetExportSession` (iOS), with an in-editor progress spinner and cancel button that deletes the partial output file. `EditorResult` now returns real `width`/`height`/`duration`/`fileSize`/`mimeType` for video exports too. Added the `androidx.media3` dependency family (exoplayer/ui/transformer/effect/common, resolved live over the network). Aspect-ratio crop is Android-only for now — deliberately not shipped on iOS, where the transform math to combine it correctly with rotation was not verifiable without a device.
|
|
17
|
+
- Add selected-state highlighting for every mode toolbar button (Crop/Filters/Stickers/Draw on photo, Crop/Mute on video) and every chip in a tool's sub-bar (aspect presets, filter adjustment/preset chips, layer property chips) so the active one is visible at a glance, using `primaryColor` text on a low-alpha `primaryColor` background.
|
|
18
|
+
- Add playback speed control (Milestone 6, in progress): cycles 0.5x/1x/1.5x/2x on the preview player (`ExoPlayer.setPlaybackSpeed` / `AVPlayer.rate`). Export does not yet honor the selected speed — a deliberate, documented scope cut (see `docs/video-editor.md`) rather than an oversight, since getting Media3/AVFoundation speed-change-at-export math right was not verifiable without a device.
|
|
19
|
+
- Add timed text/sticker/shape overlays on video (Milestone 6): reuses the exact `PhotoLayer`/`PhotoLayerStack`/`PhotoLayerRenderer` machinery from photo layers rather than a parallel system (added `startMs`/`endMs` + `isActiveAt`/`isActive(atMs:)` to `PhotoLayer`). Preview composites active overlays over the player on the existing position-poll tick; export burns them in via Media3 `OverlayEffect`/`BitmapOverlay` (Android) or `AVVideoCompositionCoreAnimationTool` (iOS). Header Undo/Redo now works for video too. Scoped to full-duration overlays only for this pass — no per-overlay time-range trim UI or drag/scale/rotate/delete yet (the largest remaining Milestone 6 item), which let the export side flatten every overlay into one static composited image instead of a genuinely time-varying one.
|
|
20
|
+
- Add video-level Brightness/Contrast/Saturation filters (Milestone 6, Android only, export-only — no live preview): applied via `androidx.media3.effect.Brightness`/`Contrast`/`HslAdjustment`, verified to exist in the pinned Media3 1.4.1 by inspecting the actual library bytecode (`javap`) before writing any code, rather than guessing. iOS is scoped out — per-frame CIFilter application needs a different AVFoundation composition pathway (`AVMutableVideoComposition(asset:applyingCIFiltersWithHandler:)`) that conflicts with the manual layer-instruction composition already used for rotate/flip/crop; combining them would need a custom `AVVideoCompositing` implementation.
|
|
21
|
+
- An attempt to also apply `speed` at export (via `SpeedProvider`/`TimestampAdjustment`) found that API isn't in Media3 1.4.1; bumping to 1.9.4 to get it broke three other already-verified call sites, so the bump was reverted and `speed` stays preview-only, documented in `docs/video-editor.md`.
|
|
22
|
+
- Add video overlay selection, transform, and time-range editing (Milestone 6, closing the last gap): tap an overlay on the video preview to select it, reusing the exact `LayerOverlayView` built for photo layers. A mini toolbar offers Scale/Rotate/Opacity (shared slider) plus Start/End chips to scrub the overlay's time range, and Delete. Android export now genuinely respects per-overlay time ranges via a `BitmapOverlay.getBitmap(presentationTimeUs:)` callback that recomputes and caches the active layer set per timestamp (previously every overlay was flattened into one static full-duration image); iOS gets the same editing UI but its export still burns overlays in for the whole clip regardless of range — documented as the one remaining Milestone 6 gap, since fixing it needs a custom `AVVideoCompositing` implementation.
|
|
23
|
+
- Add a multi-clip timeline (Milestone 7, partial — audio tracks and transitions deliberately deferred per the master spec's own sequencing advice): the single implicit video is replaced by an ordered `VideoClip` list (id/source/trim), with a clip-strip UI (Split at the playhead/Duplicate/Delete/Move-Left/Move-Right) and the trim range view now bound to whichever clip is selected instead of the whole video. Android plays a genuine multi-item ExoPlayer playlist and adds `VideoEditSession.globalPositionMs()`/`seekToGlobalMs()` to bridge the composed timeline and the player's own per-item position; export concatenates every clip into one `EditedMediaItemSequence`/`Composition`, verified against the actual Media3 1.4.1 bytecode (`javap`) before writing the code. iOS takes an architecturally simpler route — both preview and export concatenate every clip into a single `AVMutableComposition` via native multi-segment `insertTimeRange(_:of:at:)`, so `player.currentTime()`/`.seek(to:)` already are the composed-timeline position with no separate global-position bookkeeping needed, unlike Android's playlist approach. Gradle-verified on Android (`compileDebugKotlin`, `assembleDebug` for both the library and example app); iOS reviewed but not locally built (no macOS/Xcode here). Every clip in this pass still shares one source file — importing separate additional source files is a follow-up. See `docs/video-editor.md` for the full gap list.
|
|
24
|
+
|
|
25
|
+
## 0.1.0
|
|
26
|
+
|
|
27
|
+
- Initial Turbo Module foundation and typed placeholder contract.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Contributions are always welcome, no matter how large or small!
|
|
4
|
+
|
|
5
|
+
We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md).
|
|
6
|
+
|
|
7
|
+
## Development workflow
|
|
8
|
+
|
|
9
|
+
This project is a monorepo managed using [Yarn workspaces](https://yarnpkg.com/features/workspaces). It contains the following packages:
|
|
10
|
+
|
|
11
|
+
- The library package in the root directory.
|
|
12
|
+
- An example app in the `example/` directory.
|
|
13
|
+
|
|
14
|
+
To get started with the project, make sure you have the correct version of [Node.js](https://nodejs.org/) installed. See the [`.nvmrc`](./.nvmrc) file for the version used in this project.
|
|
15
|
+
|
|
16
|
+
Run `yarn` in the root directory to install the required dependencies for each package:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
yarn
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
> Since the project relies on Yarn workspaces, you cannot use [`npm`](https://github.com/npm/cli) for development without manually migrating.
|
|
23
|
+
|
|
24
|
+
The [example app](/example/) demonstrates usage of the library. You need to run it to test any changes you make.
|
|
25
|
+
|
|
26
|
+
It is configured to use the local version of the library, so any changes you make to the library's source code will be reflected in the example app. Changes to the library's JavaScript code will be reflected in the example app without a rebuild, but native code changes will require a rebuild of the example app.
|
|
27
|
+
|
|
28
|
+
If you want to use Android Studio or Xcode to edit the native code, you can open the `example/android` or `example/ios` directories respectively in those editors. To edit the Objective-C or Swift files, open `example/ios/PhotoVideoEditorExample.xcworkspace` in Xcode and find the source files at `Pods > Development Pods > react-native-photo-video-editor`.
|
|
29
|
+
|
|
30
|
+
To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-photo-video-editor` under `Android`.
|
|
31
|
+
|
|
32
|
+
You can use various commands from the root directory to work with the project.
|
|
33
|
+
|
|
34
|
+
To start the packager:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
yarn example start
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
To run the example app on Android:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
yarn example android
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
To run the example app on iOS:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
yarn example ios
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
To confirm that the app is running with the new architecture, you can check the Metro logs for a message like this:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
Running "PhotoVideoEditorExample" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Note the `"fabric":true` and `"concurrentRoot":true` properties.
|
|
59
|
+
|
|
60
|
+
Make sure your code passes TypeScript:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
yarn typecheck
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
To check for linting errors, run the following:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
yarn lint
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
To fix formatting errors, run the following:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
yarn lint --fix
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
### Scripts
|
|
81
|
+
|
|
82
|
+
The `package.json` file contains various scripts for common tasks:
|
|
83
|
+
|
|
84
|
+
- `yarn`: setup project by installing dependencies.
|
|
85
|
+
- `yarn typecheck`: type-check files with TypeScript.
|
|
86
|
+
- `yarn lint`: lint files with [ESLint](https://eslint.org/).
|
|
87
|
+
- `yarn example start`: start the Metro server for the example app.
|
|
88
|
+
- `yarn example android`: run the example app on Android.
|
|
89
|
+
- `yarn example ios`: run the example app on iOS.
|
|
90
|
+
|
|
91
|
+
### Sending a pull request
|
|
92
|
+
|
|
93
|
+
> **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).
|
|
94
|
+
|
|
95
|
+
When you're sending a pull request:
|
|
96
|
+
|
|
97
|
+
- Prefer small pull requests focused on one change.
|
|
98
|
+
- Verify that linters and tests are passing.
|
|
99
|
+
- Review the documentation to make sure it looks good.
|
|
100
|
+
- Follow the pull request template when opening a pull request.
|
|
101
|
+
- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AUTHOR_NAME_PLACEHOLDER
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "PhotoVideoEditor"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/Babur171/react-native-photo-video-editor.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
|
+
s.private_header_files = "ios/**/*.h"
|
|
18
|
+
# ios/Tests holds XCTest-based unit tests, not library sources — XCTest isn't linked into
|
|
19
|
+
# consumer app targets, so these must never ship as part of the pod's normal build.
|
|
20
|
+
s.exclude_files = "ios/Tests/**/*"
|
|
21
|
+
|
|
22
|
+
install_modules_dependencies(s)
|
|
23
|
+
end
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# react-native-photo-video-editor
|
|
2
|
+
|
|
3
|
+
> Early development: Milestone 6 is complete on Android and nearly complete on iOS, and Milestone 7's multi-clip timeline slice (clip list, split/duplicate/delete/reorder, gapless multi-clip export) is now complete on both platforms — audio tracks and transitions are deliberately deferred to a later slice. Android is compile-verified via Gradle throughout, including live-resolved Media3 dependencies, a time-aware `OverlayEffect` export path, the `Brightness`/`Contrast`/`HslAdjustment` filter export path, and multi-clip export via `EditedMediaItemSequence`/`Composition` (all verified against actual library bytecode, not guessed); iOS implements the same editing UI via AVFoundation but is unbuilt (no macOS toolchain here), uses a simpler `AVMutableComposition`-based approach for multi-clip preview/export, and its overlay export still burns overlays in for the whole clip regardless of their configured time range — the one remaining Milestone 6 gap, needing a custom `AVVideoCompositing` implementation to fix. Video aspect-ratio crop, video-level filters, and export-time speed change are Android-only/not-yet-implemented, each a documented, deliberate scope cut rather than an oversight.
|
|
4
|
+
|
|
5
|
+
A React Native Android/iOS Turbo Module with focused `openPhotoEditor`, `openVideoEditor`, and unified `openEditor` APIs. The full-screen native shell previews local media, honors feature visibility and theme, edits photos on-device (crop/rotate/straighten, adjustments/filters, and text layers) and video on-device (trim/rotate/speed/text/filters/export), and returns a typed result with real output metadata on Done or a typed cancellation result on Cancel. See [docs/photo-editor.md](docs/photo-editor.md) and [docs/video-editor.md](docs/video-editor.md) for details.
|
|
6
|
+
|
|
7
|
+
## Requirements and installation
|
|
8
|
+
|
|
9
|
+
React Native 0.85-compatible New Architecture applications are the verified target of this scaffold. React and React Native are peer dependencies. Autolinking handles native registration.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install react-native-photo-video-editor
|
|
13
|
+
# or: yarn add react-native-photo-video-editor
|
|
14
|
+
# or: pnpm add react-native-photo-video-editor
|
|
15
|
+
cd ios && pod install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
No permissions are required by the placeholder. Future gallery saving will require consumer-app Photos/media usage descriptions or permissions; the library will not add broad storage permissions.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import {
|
|
24
|
+
openVideoEditor,
|
|
25
|
+
cancelExport,
|
|
26
|
+
addExportProgressListener,
|
|
27
|
+
PhotoVideoEditorError,
|
|
28
|
+
} from 'react-native-photo-video-editor';
|
|
29
|
+
|
|
30
|
+
const subscription = addExportProgressListener((event) => {
|
|
31
|
+
console.log(event.jobId, event.progress); // progress: 0..1
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const result = await openVideoEditor({
|
|
36
|
+
source: { uri: 'file:///path/to/video.mp4', type: 'video' },
|
|
37
|
+
features: { crop: true, rotate: true, trim: true },
|
|
38
|
+
export: { quality: 'high', videoFormat: 'mp4', frameRate: 30 },
|
|
39
|
+
saveToGallery: false,
|
|
40
|
+
});
|
|
41
|
+
console.log(result.uri, result.cancelled);
|
|
42
|
+
await cancelExport();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error instanceof PhotoVideoEditorError)
|
|
45
|
+
console.error(error.code, error.message);
|
|
46
|
+
} finally {
|
|
47
|
+
subscription.remove();
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
See [API](docs/api.md), [architecture](docs/architecture.md), [photo editor](docs/photo-editor.md), [video editor](docs/video-editor.md), and [roadmap](docs/roadmap.md). Known limitations are non-functional tool placeholders, no transformed export, local-URI-only input, and no verified legacy-architecture support.
|
|
52
|
+
|
|
53
|
+
For local work, run `yarn`, then `yarn typecheck`, `yarn lint`, `yarn test`, and `yarn prepare`. See [CONTRIBUTING.md](CONTRIBUTING.md). MIT licensed; see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.PhotoVideoEditor = [
|
|
3
|
+
kotlinVersion: "2.0.21",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
ext.getExtOrDefault = { prop ->
|
|
9
|
+
if (rootProject.ext.has(prop)) {
|
|
10
|
+
return rootProject.ext.get(prop)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return PhotoVideoEditor[prop]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
repositories {
|
|
17
|
+
google()
|
|
18
|
+
mavenCentral()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
dependencies {
|
|
22
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
23
|
+
// noinspection DifferentKotlinGradleVersion
|
|
24
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
apply plugin: "com.android.library"
|
|
30
|
+
apply plugin: "kotlin-android"
|
|
31
|
+
|
|
32
|
+
apply plugin: "com.facebook.react"
|
|
33
|
+
|
|
34
|
+
android {
|
|
35
|
+
namespace "com.photovideoeditor"
|
|
36
|
+
|
|
37
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
38
|
+
|
|
39
|
+
defaultConfig {
|
|
40
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
compileOptions {
|
|
44
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
45
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
dependencies {
|
|
50
|
+
implementation "com.facebook.react:react-android"
|
|
51
|
+
implementation "androidx.exifinterface:exifinterface:1.3.7"
|
|
52
|
+
// Grid layout for the online-stickers browse sheet (Stickers > Browse online).
|
|
53
|
+
implementation "androidx.recyclerview:recyclerview:1.3.2"
|
|
54
|
+
// Keep the editor on the same Media3 ABI used by react-native-video.
|
|
55
|
+
implementation "androidx.media3:media3-exoplayer:1.8.0"
|
|
56
|
+
implementation "androidx.media3:media3-ui:1.8.0"
|
|
57
|
+
implementation "androidx.media3:media3-transformer:1.8.0"
|
|
58
|
+
implementation "androidx.media3:media3-effect:1.8.0"
|
|
59
|
+
implementation "androidx.media3:media3-common:1.8.0"
|
|
60
|
+
testImplementation "junit:junit:4.13.2"
|
|
61
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
3
|
+
|
|
4
|
+
<application>
|
|
5
|
+
<activity
|
|
6
|
+
android:name=".editor.PhotoVideoEditorActivity"
|
|
7
|
+
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
|
|
8
|
+
android:exported="false"
|
|
9
|
+
android:screenOrientation="portrait"
|
|
10
|
+
android:theme="@style/PhotoVideoEditorTheme" />
|
|
11
|
+
</application>
|
|
12
|
+
</manifest>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
package com.photovideoeditor
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.Promise
|
|
7
|
+
import com.facebook.react.bridge.BaseActivityEventListener
|
|
8
|
+
import com.photovideoeditor.editor.PhotoVideoEditorActivity
|
|
9
|
+
import com.photovideoeditor.model.EditorRequest
|
|
10
|
+
import com.photovideoeditor.validation.EditorRequestValidator
|
|
11
|
+
import org.json.JSONObject
|
|
12
|
+
|
|
13
|
+
class PhotoVideoEditorModule(reactContext: ReactApplicationContext) :
|
|
14
|
+
NativePhotoVideoEditorSpec(reactContext) {
|
|
15
|
+
|
|
16
|
+
private var editorPromise: Promise? = null
|
|
17
|
+
private var pendingUri: String? = null
|
|
18
|
+
private var pendingType: String? = null
|
|
19
|
+
private var pendingMimeType: String? = null
|
|
20
|
+
|
|
21
|
+
private val activityEventListener = object : BaseActivityEventListener() {
|
|
22
|
+
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
|
|
23
|
+
if (requestCode != EDITOR_REQUEST_CODE) return
|
|
24
|
+
val promise = editorPromise ?: return
|
|
25
|
+
editorPromise = null
|
|
26
|
+
|
|
27
|
+
if (resultCode == PhotoVideoEditorActivity.RESULT_ERROR) {
|
|
28
|
+
val code = data?.getStringExtra(PhotoVideoEditorActivity.EXTRA_ERROR_CODE) ?: "E_INTERNAL"
|
|
29
|
+
promise.reject(code, data?.getStringExtra(PhotoVideoEditorActivity.EXTRA_ERROR) ?: "The editor could not open the selected media.")
|
|
30
|
+
pendingUri = null
|
|
31
|
+
pendingType = null
|
|
32
|
+
pendingMimeType = null
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
val cancelled = resultCode != Activity.RESULT_OK
|
|
37
|
+
val result = JSONObject()
|
|
38
|
+
.put("uri", data?.getStringExtra(PhotoVideoEditorActivity.EXTRA_URI) ?: pendingUri.orEmpty())
|
|
39
|
+
.put("type", data?.getStringExtra(PhotoVideoEditorActivity.EXTRA_TYPE) ?: pendingType.orEmpty())
|
|
40
|
+
.put("cancelled", cancelled)
|
|
41
|
+
if (!cancelled) {
|
|
42
|
+
val exportedMimeType = data?.getStringExtra(PhotoVideoEditorActivity.EXTRA_MIME_TYPE) ?: pendingMimeType
|
|
43
|
+
exportedMimeType?.let { result.put("mimeType", it) }
|
|
44
|
+
if (data?.hasExtra(PhotoVideoEditorActivity.EXTRA_WIDTH) == true) {
|
|
45
|
+
result.put("width", data.getIntExtra(PhotoVideoEditorActivity.EXTRA_WIDTH, 0))
|
|
46
|
+
result.put("height", data.getIntExtra(PhotoVideoEditorActivity.EXTRA_HEIGHT, 0))
|
|
47
|
+
result.put("fileSize", data.getLongExtra(PhotoVideoEditorActivity.EXTRA_FILE_SIZE, 0L))
|
|
48
|
+
}
|
|
49
|
+
if (data?.hasExtra(PhotoVideoEditorActivity.EXTRA_DURATION) == true) {
|
|
50
|
+
result.put("duration", data.getLongExtra(PhotoVideoEditorActivity.EXTRA_DURATION, 0L))
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
pendingUri = null
|
|
54
|
+
pendingType = null
|
|
55
|
+
pendingMimeType = null
|
|
56
|
+
emitOnEditorEvent(JSONObject().put("type", "editorClosed").toString())
|
|
57
|
+
promise.resolve(result.toString())
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
init {
|
|
62
|
+
reactContext.addActivityEventListener(activityEventListener)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override fun openEditor(request: String, promise: Promise) {
|
|
66
|
+
try {
|
|
67
|
+
val payload = JSONObject(request)
|
|
68
|
+
val source = payload.optJSONObject("source")
|
|
69
|
+
if (source == null) {
|
|
70
|
+
promise.reject("E_INVALID_OPTIONS", "options.source is required.")
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
val uri = source.optString("uri").trim()
|
|
74
|
+
val type = source.optString("type")
|
|
75
|
+
val mimeType = source.optString("mimeType").takeIf { it.isNotBlank() }
|
|
76
|
+
val validationCode = EditorRequestValidator.validate(EditorRequest(uri, type, mimeType))
|
|
77
|
+
if (validationCode != null) {
|
|
78
|
+
val message = if (validationCode == "E_INVALID_URI") "source.uri must be a non-empty string." else "source.type must be photo or video."
|
|
79
|
+
promise.reject(validationCode, message)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
val activity = reactApplicationContext.currentActivity
|
|
83
|
+
if (activity == null) {
|
|
84
|
+
promise.reject("E_EDITOR_UNAVAILABLE", "The editor requires a foreground Android Activity.")
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
if (editorPromise != null) {
|
|
88
|
+
promise.reject("E_EDITOR_ALREADY_OPEN", "Another editor is already open.")
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
editorPromise = promise
|
|
93
|
+
pendingUri = uri
|
|
94
|
+
pendingType = type
|
|
95
|
+
pendingMimeType = mimeType
|
|
96
|
+
val intent = Intent(activity, PhotoVideoEditorActivity::class.java)
|
|
97
|
+
.putExtra(PhotoVideoEditorActivity.EXTRA_URI, uri)
|
|
98
|
+
.putExtra(PhotoVideoEditorActivity.EXTRA_TYPE, type)
|
|
99
|
+
.putExtra(PhotoVideoEditorActivity.EXTRA_MIME_TYPE, mimeType)
|
|
100
|
+
.putExtra(PhotoVideoEditorActivity.EXTRA_REQUEST, request)
|
|
101
|
+
activity.runOnUiThread {
|
|
102
|
+
try {
|
|
103
|
+
activity.startActivityForResult(intent, EDITOR_REQUEST_CODE)
|
|
104
|
+
emitOnEditorEvent(JSONObject().put("type", "editorOpened").toString())
|
|
105
|
+
} catch (error: Exception) {
|
|
106
|
+
editorPromise = null
|
|
107
|
+
pendingUri = null
|
|
108
|
+
pendingType = null
|
|
109
|
+
pendingMimeType = null
|
|
110
|
+
promise.reject("E_INTERNAL", "Unable to launch the editor.", error)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (_: Exception) {
|
|
114
|
+
promise.reject("E_INVALID_OPTIONS", "Editor options must be valid serialized JSON.")
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun cancelExport(jobId: String?, promise: Promise) = promise.resolve(false)
|
|
119
|
+
|
|
120
|
+
override fun isAvailable(): Boolean = true
|
|
121
|
+
|
|
122
|
+
companion object {
|
|
123
|
+
const val NAME = NativePhotoVideoEditorSpec.NAME
|
|
124
|
+
private const val EDITOR_REQUEST_CODE = 41091
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.photovideoeditor
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
import java.util.HashMap
|
|
9
|
+
|
|
10
|
+
class PhotoVideoEditorPackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
|
+
return if (name == PhotoVideoEditorModule.NAME) {
|
|
13
|
+
PhotoVideoEditorModule(reactContext)
|
|
14
|
+
} else {
|
|
15
|
+
null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
|
|
20
|
+
mapOf(
|
|
21
|
+
PhotoVideoEditorModule.NAME to ReactModuleInfo(
|
|
22
|
+
name = PhotoVideoEditorModule.NAME,
|
|
23
|
+
className = PhotoVideoEditorModule.NAME,
|
|
24
|
+
canOverrideExistingModule = false,
|
|
25
|
+
needsEagerInit = false,
|
|
26
|
+
isCxxModule = false,
|
|
27
|
+
isTurboModule = true
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|