@umituz/react-native-video-editor 1.0.32 → 1.0.34
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/package.json +12 -5
- package/src/infrastructure/services/image-layer-operations.service.ts +1 -1
- package/src/infrastructure/services/layer-operations/layer-duplicate.service.ts +1 -1
- package/src/infrastructure/services/scene-operations.service.ts +1 -1
- package/src/infrastructure/services/shape-layer-operations.service.ts +1 -1
- package/src/infrastructure/services/text-layer-operations.service.ts +1 -1
- package/src/player/infrastructure/services/player-control.service.ts +2 -2
- package/src/player/infrastructure/services/video-cache.service.ts +1 -1
- package/src/player/presentation/hooks/useVideoPlayerControl.ts +2 -2
- package/src/presentation/components/AnimationEditor.tsx +1 -1
- package/src/presentation/components/AudioEditor.tsx +1 -1
- package/src/presentation/components/DraggableLayer.tsx +2 -3
- package/src/presentation/components/EditorHeader.tsx +0 -1
- package/src/presentation/components/EditorPreviewArea.styles.ts +1 -1
- package/src/presentation/components/EditorToolPanel.tsx +0 -1
- package/src/presentation/components/ExportDialog.tsx +0 -3
- package/src/presentation/components/LayerActionsMenu.tsx +1 -1
- package/src/presentation/components/SceneActionsMenu.tsx +1 -1
- package/src/presentation/components/ShapeLayerEditor.tsx +0 -6
- package/src/presentation/components/draggable-layer/ResizeHandles.tsx +1 -1
- package/src/presentation/components/shape-layer/ShapePreview.tsx +0 -1
- package/src/presentation/components/text-layer/EditorActions.tsx +0 -1
- package/src/presentation/hooks/useEditorBottomSheet.ts +0 -1
- package/src/presentation/hooks/useEditorHistory.ts +35 -40
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-video-editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.34",
|
|
4
4
|
"description": "Professional video editor with layer-based timeline, text/image/shape/audio/animation layers, and export functionality",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"typecheck": "npx tsc --noEmit",
|
|
13
|
-
"lint": "
|
|
13
|
+
"lint": "eslint src",
|
|
14
|
+
"lint:fix": "eslint src --fix",
|
|
14
15
|
"version:patch": "npm version patch -m 'chore: release v%s'",
|
|
15
16
|
"version:minor": "npm version minor -m 'chore: release v%s'",
|
|
16
17
|
"version:major": "npm version major -m 'chore: release v%s'"
|
|
@@ -30,7 +31,6 @@
|
|
|
30
31
|
"url": "https://github.com/umituz/react-native-video-editor"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@umituz/react-native-filesystem": "latest",
|
|
34
34
|
"expo-document-picker": ">=14.0.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
@@ -47,18 +47,25 @@
|
|
|
47
47
|
"@gorhom/bottom-sheet": "^5.2.8",
|
|
48
48
|
"@types/react": "~19.1.10",
|
|
49
49
|
"@umituz/react-native-design-system": "latest",
|
|
50
|
-
"@umituz/react-native-
|
|
50
|
+
"@umituz/react-native-localization": "latest",
|
|
51
|
+
"@umituz/react-native-uuid": "latest",
|
|
51
52
|
"expo-application": "^7.0.8",
|
|
53
|
+
"expo-crypto": "^13.0.2",
|
|
52
54
|
"expo-device": "^8.0.10",
|
|
53
55
|
"expo-document-picker": "^14.0.8",
|
|
54
56
|
"expo-file-system": "^19.0.0",
|
|
55
57
|
"expo-image": "^3.0.11",
|
|
58
|
+
"expo-sharing": "^14.0.8",
|
|
56
59
|
"expo-video": "^3.0.15",
|
|
57
60
|
"react": "19.1.0",
|
|
58
61
|
"react-native": "0.81.5",
|
|
59
62
|
"react-native-gesture-handler": "^2.30.0",
|
|
60
63
|
"react-native-reanimated": "^4.2.1",
|
|
61
|
-
"typescript": "~5.9.2"
|
|
64
|
+
"typescript": "~5.9.2",
|
|
65
|
+
"@types/react-native": "*",
|
|
66
|
+
"@typescript-eslint/parser": "*",
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "*",
|
|
68
|
+
"eslint": "*"
|
|
62
69
|
},
|
|
63
70
|
"publishConfig": {
|
|
64
71
|
"access": "public"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Single Responsibility: Image layer business logic
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { generateUUID } from "@umituz/react-native-
|
|
6
|
+
import { generateUUID } from "@umituz/react-native-uuid";
|
|
7
7
|
import type { Scene, ImageLayer } from "../../domain/entities";
|
|
8
8
|
import type { LayerOperationResult, AddImageLayerData } from "../../domain/entities";
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Single Responsibility: Handle layer duplication operations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { generateUUID } from "@umituz/react-native-
|
|
6
|
+
import { generateUUID } from "@umituz/react-native-uuid";
|
|
7
7
|
import type { Scene } from "../../../domain/entities";
|
|
8
8
|
import type { LayerOperationResult } from "../../../domain/entities";
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Single Responsibility: Business logic for scene operations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { generateUUID } from "@umituz/react-native-
|
|
6
|
+
import { generateUUID } from "@umituz/react-native-uuid";
|
|
7
7
|
import type { Scene, Audio } from "../../domain/entities";
|
|
8
8
|
import type { SceneOperationResult } from "../../domain/entities";
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Single Responsibility: Shape layer business logic
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { generateUUID } from "@umituz/react-native-
|
|
6
|
+
import { generateUUID } from "@umituz/react-native-uuid";
|
|
7
7
|
import type { Scene, ShapeLayer } from "../../domain/entities";
|
|
8
8
|
import type { LayerOperationResult, AddShapeLayerData } from "../../domain/entities";
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Single Responsibility: Text layer business logic
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { generateUUID } from "@umituz/react-native-
|
|
6
|
+
import { generateUUID } from "@umituz/react-native-uuid";
|
|
7
7
|
import type { Scene, TextLayer } from "../../domain/entities";
|
|
8
8
|
import type { LayerOperationResult, AddTextLayerData } from "../../domain/entities";
|
|
9
9
|
|
|
@@ -44,7 +44,7 @@ export const safePause = (player: VideoPlayer | null): boolean => {
|
|
|
44
44
|
return true;
|
|
45
45
|
} catch (error) {
|
|
46
46
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
console.log("[VideoPlayer] Pause error ignored:", error);
|
|
49
49
|
}
|
|
50
50
|
return false;
|
|
@@ -96,7 +96,7 @@ export const configurePlayer = (
|
|
|
96
96
|
}
|
|
97
97
|
} catch (error) {
|
|
98
98
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
console.log("[VideoPlayer] Configure error ignored:", error);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
@@ -35,7 +35,7 @@ export const useVideoPlayerControl = (
|
|
|
35
35
|
|
|
36
36
|
const player = useExpoVideoPlayer(source || "", (p) => {
|
|
37
37
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
console.log("[useVideoPlayerControl] Player callback, source:", source, "player:", !!p);
|
|
40
40
|
}
|
|
41
41
|
if (source && p) {
|
|
@@ -45,7 +45,7 @@ export const useVideoPlayerControl = (
|
|
|
45
45
|
setIsPlaying(true);
|
|
46
46
|
}
|
|
47
47
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
console.log("[useVideoPlayerControl] Player status:", {
|
|
50
50
|
currentTime: p.currentTime,
|
|
51
51
|
duration: p.duration,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { View, ScrollView, StyleSheet } from "react-native";
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
import type { Animation } from "../../domain/entities";
|
|
10
10
|
import { useAnimationLayerForm } from "../hooks/useAnimationLayerForm";
|
|
11
11
|
import {
|
|
@@ -57,7 +57,7 @@ export const AudioEditor: React.FC<AudioEditorProps> = ({
|
|
|
57
57
|
if (!result.canceled && result.assets && result.assets.length > 0) {
|
|
58
58
|
setAudioUri(result.assets[0].uri);
|
|
59
59
|
}
|
|
60
|
-
} catch
|
|
60
|
+
} catch {
|
|
61
61
|
Alert.alert(t("audio.errors.pickFailed"));
|
|
62
62
|
}
|
|
63
63
|
}, [setAudioUri, t]);
|
|
@@ -63,17 +63,16 @@ export const DraggableLayer: React.FC<DraggableLayerProps> = ({
|
|
|
63
63
|
|
|
64
64
|
const animatedStyle = useAnimatedStyle(() => {
|
|
65
65
|
const rotationStr = `${layer.rotation}deg`;
|
|
66
|
-
|
|
66
|
+
return {
|
|
67
67
|
transform: [
|
|
68
68
|
{ translateX: translateX.value },
|
|
69
69
|
{ translateY: translateY.value },
|
|
70
70
|
{ rotate: rotationStr },
|
|
71
|
-
]
|
|
71
|
+
],
|
|
72
72
|
opacity: layer.opacity,
|
|
73
73
|
width: width.value,
|
|
74
74
|
height: height.value,
|
|
75
75
|
};
|
|
76
|
-
return style as any;
|
|
77
76
|
});
|
|
78
77
|
|
|
79
78
|
return (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StyleSheet } from "react-native";
|
|
2
2
|
import { DesignTokens } from "@umituz/react-native-design-system";
|
|
3
3
|
|
|
4
|
-
export const createPreviewStyles = (
|
|
4
|
+
export const createPreviewStyles = (_tokens: DesignTokens) =>
|
|
5
5
|
StyleSheet.create({
|
|
6
6
|
previewSection: {
|
|
7
7
|
padding: 16,
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
useAppDesignTokens,
|
|
18
18
|
} from "@umituz/react-native-design-system";
|
|
19
19
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
20
|
-
import type { Audio } from "../../domain/entities";
|
|
21
20
|
|
|
22
21
|
export interface EditorToolPanelProps {
|
|
23
22
|
onAddText: () => void;
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useAppDesignTokens,
|
|
12
12
|
} from "@umituz/react-native-design-system";
|
|
13
13
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
14
|
-
import type { Layer
|
|
14
|
+
import type { Layer } from "../../domain/entities";
|
|
15
15
|
|
|
16
16
|
export interface LayerActionsMenuProps {
|
|
17
17
|
layer: Layer;
|
|
@@ -5,11 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
7
|
import { View, ScrollView, StyleSheet } from "react-native";
|
|
8
|
-
import {
|
|
9
|
-
AtomicText,
|
|
10
|
-
AtomicIcon,
|
|
11
|
-
useAppDesignTokens,
|
|
12
|
-
} from "@umituz/react-native-design-system";
|
|
13
8
|
import type { ShapeLayer } from "../../domain/entities";
|
|
14
9
|
import { useShapeLayerForm } from "../hooks/useShapeLayerForm";
|
|
15
10
|
import {
|
|
@@ -35,7 +30,6 @@ export const ShapeLayerEditor: React.FC<ShapeLayerEditorProps> = ({
|
|
|
35
30
|
onSave,
|
|
36
31
|
onCancel,
|
|
37
32
|
}) => {
|
|
38
|
-
const tokens = useAppDesignTokens();
|
|
39
33
|
const {
|
|
40
34
|
formState,
|
|
41
35
|
setShape,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import {
|
|
7
|
+
import { StyleSheet } from "react-native";
|
|
8
8
|
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
9
9
|
import Animated from "react-native-reanimated";
|
|
10
10
|
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
AtomicText,
|
|
10
10
|
useAppDesignTokens,
|
|
11
11
|
} from "@umituz/react-native-design-system";
|
|
12
|
-
import type { ShapeType } from "../../../infrastructure/constants/shape-layer.constants";
|
|
13
12
|
import type { ShapeLayerFormState } from "../../hooks/useShapeLayerForm";
|
|
14
13
|
|
|
15
14
|
interface ShapePreviewProps {
|
|
@@ -7,7 +7,6 @@ import React from "react";
|
|
|
7
7
|
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
8
8
|
import {
|
|
9
9
|
AtomicText,
|
|
10
|
-
AtomicIcon,
|
|
11
10
|
useAppDesignTokens,
|
|
12
11
|
} from "@umituz/react-native-design-system";
|
|
13
12
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
@@ -3,24 +3,12 @@
|
|
|
3
3
|
* Single Responsibility: History operations for editor
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { useCallback } from "react";
|
|
7
|
-
import { Alert } from "react-native";
|
|
8
|
-
import { useLocalization } from "@umituz/react-native-localization";
|
|
9
|
-
// TODO: Refactor to use TanStack Query instead of store
|
|
10
|
-
// Temporary stub until refactor
|
|
11
|
-
const useHistoryStore = () => ({
|
|
12
|
-
addToHistory: () => {},
|
|
13
|
-
pushHistory: (_project: VideoProject | undefined, _action: string) => {},
|
|
14
|
-
undo: () => undefined,
|
|
15
|
-
redo: () => undefined,
|
|
16
|
-
canUndo: () => false,
|
|
17
|
-
canRedo: () => false,
|
|
18
|
-
});
|
|
6
|
+
import { useCallback, useState } from "react";
|
|
19
7
|
import type { VideoProject } from "../../domain/entities";
|
|
20
8
|
|
|
21
9
|
export interface UseEditorHistoryParams {
|
|
22
10
|
project: VideoProject | undefined;
|
|
23
|
-
projectId: string;
|
|
11
|
+
projectId: string; // Kept for interface compatibility, used for reset if needed
|
|
24
12
|
onUpdateProject: (updates: Partial<VideoProject>) => void;
|
|
25
13
|
}
|
|
26
14
|
|
|
@@ -34,49 +22,56 @@ export interface UseEditorHistoryReturn {
|
|
|
34
22
|
|
|
35
23
|
export function useEditorHistory({
|
|
36
24
|
project,
|
|
37
|
-
projectId,
|
|
38
25
|
onUpdateProject,
|
|
39
26
|
}: UseEditorHistoryParams): UseEditorHistoryReturn {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
pushHistory,
|
|
43
|
-
undo: historyUndo,
|
|
44
|
-
redo: historyRedo,
|
|
45
|
-
canUndo,
|
|
46
|
-
canRedo,
|
|
47
|
-
} = useHistoryStore();
|
|
27
|
+
const [history, setHistory] = useState<VideoProject[]>([]);
|
|
28
|
+
const [future, setFuture] = useState<VideoProject[]>([]);
|
|
48
29
|
|
|
49
30
|
const updateWithHistory = useCallback(
|
|
50
|
-
(updates: Partial<VideoProject>,
|
|
31
|
+
(updates: Partial<VideoProject>, _action: string) => {
|
|
51
32
|
if (project) {
|
|
52
|
-
|
|
33
|
+
// Push current state to history before updating
|
|
34
|
+
setHistory((prev) => [...prev, project]);
|
|
35
|
+
// Clear future
|
|
36
|
+
setFuture([]);
|
|
37
|
+
|
|
53
38
|
onUpdateProject(updates);
|
|
54
39
|
}
|
|
55
40
|
},
|
|
56
|
-
[project,
|
|
41
|
+
[project, onUpdateProject],
|
|
57
42
|
);
|
|
58
43
|
|
|
59
44
|
const undo = useCallback(() => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
45
|
+
if (history.length === 0 || !project) return;
|
|
46
|
+
|
|
47
|
+
const previousState = history[history.length - 1];
|
|
48
|
+
const newHistory = history.slice(0, -1);
|
|
49
|
+
|
|
50
|
+
setFuture((prev) => [project, ...prev]);
|
|
51
|
+
setHistory(newHistory);
|
|
52
|
+
|
|
53
|
+
// Full replacement of state
|
|
54
|
+
// We assume onUpdateProject can handle a full object which is a superset of Partial
|
|
55
|
+
onUpdateProject(previousState);
|
|
56
|
+
}, [history, project, onUpdateProject]);
|
|
66
57
|
|
|
67
58
|
const redo = useCallback(() => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
if (future.length === 0 || !project) return;
|
|
60
|
+
|
|
61
|
+
const nextState = future[0];
|
|
62
|
+
const newFuture = future.slice(1);
|
|
63
|
+
|
|
64
|
+
setHistory((prev) => [...prev, project]);
|
|
65
|
+
setFuture(newFuture);
|
|
66
|
+
|
|
67
|
+
onUpdateProject(nextState);
|
|
68
|
+
}, [future, project, onUpdateProject]);
|
|
74
69
|
|
|
75
70
|
return {
|
|
76
71
|
undo,
|
|
77
72
|
redo,
|
|
78
|
-
canUndo:
|
|
79
|
-
canRedo:
|
|
73
|
+
canUndo: history.length > 0,
|
|
74
|
+
canRedo: future.length > 0,
|
|
80
75
|
updateWithHistory,
|
|
81
76
|
};
|
|
82
77
|
}
|