@umituz/react-native-video-editor 1.0.26 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/package.json +1 -2
  2. package/src/player/presentation/components/VideoPlayer.tsx +2 -3
  3. package/src/presentation/components/AudioEditor.tsx +15 -23
  4. package/src/presentation/components/EditorTimeline.tsx +1 -1
  5. package/src/presentation/components/EditorToolPanel.tsx +2 -2
  6. package/src/presentation/components/LayerActionsMenu.tsx +19 -12
  7. package/src/presentation/components/animation-layer/AnimationEditorActions.tsx +1 -1
  8. package/src/presentation/components/animation-layer/AnimationTypeSelector.tsx +1 -1
  9. package/src/presentation/components/audio-layer/AudioEditorActions.tsx +1 -1
  10. package/src/presentation/components/audio-layer/AudioFileSelector.tsx +7 -2
  11. package/src/presentation/components/audio-layer/FadeEffectsSelector.tsx +2 -2
  12. package/src/presentation/components/audio-layer/VolumeSelector.tsx +1 -1
  13. package/src/presentation/components/draggable-layer/LayerContent.tsx +1 -2
  14. package/src/presentation/components/draggable-layer/ResizeHandles.tsx +16 -5
  15. package/src/presentation/components/export/ExportActions.tsx +1 -1
  16. package/src/presentation/components/export/ExportProgress.tsx +6 -2
  17. package/src/presentation/components/export/OptionSelectorRow.tsx +1 -1
  18. package/src/presentation/components/export/WatermarkToggle.tsx +1 -1
  19. package/src/presentation/components/image-layer/OpacitySelector.tsx +1 -1
  20. package/src/presentation/components/shape-layer/ColorPickerHorizontal.tsx +1 -1
  21. package/src/presentation/components/shape-layer/ShapeTypeSelector.tsx +1 -1
  22. package/src/presentation/components/shape-layer/ValueSelector.tsx +1 -1
  23. package/src/presentation/components/text-layer/EditorActions.tsx +1 -1
  24. package/src/presentation/components/text-layer/FontSizeSelector.tsx +1 -1
  25. package/src/presentation/hooks/useEditorHistory.ts +7 -5
  26. package/src/presentation/hooks/useEditorScenes.ts +19 -16
  27. package/src/presentation/hooks/useImageLayerOperations.ts +12 -10
  28. package/src/presentation/hooks/useLayerActions.tsx +21 -15
  29. package/src/presentation/hooks/useLayerManipulation.ts +25 -32
  30. package/src/presentation/hooks/useShapeLayerForm.ts +1 -1
  31. package/src/presentation/hooks/useShapeLayerOperations.ts +9 -6
  32. package/src/presentation/hooks/useTextLayerOperations.ts +12 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-video-editor",
3
- "version": "1.0.26",
3
+ "version": "1.0.30",
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",
@@ -53,7 +53,6 @@
53
53
  "expo-document-picker": "^14.0.8",
54
54
  "expo-file-system": "^19.0.0",
55
55
  "expo-image": "^3.0.11",
56
- "expo-linear-gradient": "^15.0.7",
57
56
  "expo-video": "^3.0.15",
58
57
  "react": "19.1.0",
59
58
  "react-native": "0.81.5",
@@ -98,9 +98,8 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
98
98
  progressContainer: {
99
99
  ...StyleSheet.absoluteFillObject,
100
100
  justifyContent: "center", alignItems: "center",
101
- backgroundColor: "rgba(0,0,0,0.5)",
102
101
  },
103
- progressText: { color: "#fff", fontSize: 16, fontWeight: "600" },
102
+ progressText: { color: tokens.colors.onPrimary, fontSize: 16, fontWeight: "600" },
104
103
  errorText: { color: tokens.colors.error, fontSize: 14, textAlign: "center", padding: 16 },
105
104
  }), [tokens, videoWidth, videoHeight]);
106
105
 
@@ -129,7 +128,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
129
128
  ) : (
130
129
  <View style={styles.placeholder} />
131
130
  )}
132
- <View style={styles.progressContainer}>
131
+ <View style={[styles.progressContainer, { backgroundColor: "rgba(0,0,0,0.5)" }]}>
133
132
  <AtomicText style={styles.progressText}>{progressPercent}%</AtomicText>
134
133
  </View>
135
134
  </View>
@@ -5,17 +5,12 @@
5
5
 
6
6
  import React, { useCallback } from "react";
7
7
  import { View, ScrollView, StyleSheet, Alert } from "react-native";
8
- // Safe import for expo-document-picker
9
- let DocumentPicker: any;
10
- try {
11
- DocumentPicker = require("expo-document-picker");
12
- } catch (e) {
13
- // Graceful fail - DocumentPicker will be undefined
14
- }
8
+ import * as DocumentPicker from "expo-document-picker";
15
9
  import {
16
10
  AtomicText,
17
11
  useAppDesignTokens,
18
12
  } from "@umituz/react-native-design-system";
13
+ import { useLocalization } from "@umituz/react-native-localization";
19
14
  import type { Audio } from "../../domain/entities";
20
15
  import { useAudioLayerForm } from "../hooks/useAudioLayerForm";
21
16
  import { AUDIO_FILE_TYPES } from "../../infrastructure/constants/audio-layer.constants";
@@ -41,6 +36,7 @@ export const AudioEditor: React.FC<AudioEditorProps> = ({
41
36
  onCancel,
42
37
  }) => {
43
38
  const tokens = useAppDesignTokens();
39
+ const { t } = useLocalization();
44
40
  const {
45
41
  formState,
46
42
  setAudioUri,
@@ -52,10 +48,6 @@ export const AudioEditor: React.FC<AudioEditorProps> = ({
52
48
  } = useAudioLayerForm(audio);
53
49
 
54
50
  const handlePickAudio = useCallback(async () => {
55
- if (!DocumentPicker) {
56
- Alert.alert("Error", "Audio picker is not available in this environment");
57
- return;
58
- }
59
51
  try {
60
52
  const result = await DocumentPicker.getDocumentAsync({
61
53
  type: AUDIO_FILE_TYPES[0],
@@ -66,26 +58,26 @@ export const AudioEditor: React.FC<AudioEditorProps> = ({
66
58
  setAudioUri(result.assets[0].uri);
67
59
  }
68
60
  } catch (error) {
69
- Alert.alert("Error", "Failed to pick audio file");
61
+ Alert.alert(t("audio.errors.pickFailed"));
70
62
  }
71
- }, [setAudioUri]);
63
+ }, [setAudioUri, t]);
72
64
 
73
65
  const handleSave = useCallback(() => {
74
66
  if (!isValid) {
75
- Alert.alert("Error", "Please select an audio file");
67
+ Alert.alert(t("audio.errors.noFile"));
76
68
  return;
77
69
  }
78
70
  onSave(buildAudioData());
79
- }, [isValid, buildAudioData, onSave]);
71
+ }, [isValid, buildAudioData, onSave, t]);
80
72
 
81
73
  const handleRemoveAudio = useCallback(() => {
82
74
  Alert.alert(
83
- "Remove Audio",
84
- "Are you sure you want to remove the audio from this scene?",
75
+ t("audio.remove.title"),
76
+ t("audio.remove.message"),
85
77
  [
86
- { text: "Cancel", style: "cancel" },
78
+ { text: t("common.buttons.cancel"), style: "cancel" },
87
79
  {
88
- text: "Remove",
80
+ text: t("audio.remove.confirm"),
89
81
  style: "destructive",
90
82
  onPress: () => {
91
83
  onRemove?.();
@@ -93,12 +85,12 @@ export const AudioEditor: React.FC<AudioEditorProps> = ({
93
85
  },
94
86
  ],
95
87
  );
96
- }, [onRemove]);
88
+ }, [onRemove, t]);
97
89
 
98
90
  const getFileName = useCallback((uri: string) => {
99
91
  const parts = uri.split("/");
100
- return parts[parts.length - 1] || "Unknown";
101
- }, []);
92
+ return parts[parts.length - 1] || t("audio.unknownFile");
93
+ }, [t]);
102
94
 
103
95
  return (
104
96
  <View style={styles.container}>
@@ -112,7 +104,7 @@ export const AudioEditor: React.FC<AudioEditorProps> = ({
112
104
  marginBottom: 8,
113
105
  }}
114
106
  >
115
- Audio File
107
+ {t("audio.file.title")}
116
108
  </AtomicText>
117
109
  <AudioFileSelector
118
110
  audioUri={formState.audioUri}
@@ -80,7 +80,7 @@ export const EditorTimeline: React.FC<EditorTimelineProps> = ({
80
80
  >
81
81
  <AtomicText
82
82
  type="labelSmall"
83
- style={{ color: "#FFFFFF", fontWeight: "600" }}
83
+ style={{ color: tokens.colors.onPrimary, fontWeight: "600" }}
84
84
  >
85
85
  {index + 1}
86
86
  </AtomicText>
@@ -135,8 +135,8 @@ export const EditorToolPanel: React.FC<EditorToolPanelProps> = ({
135
135
  ]}
136
136
  onPress={() =>
137
137
  Alert.alert(
138
- t("editor.tools.effects", "Effects"),
139
- t("editor.tools.effectsComingSoon", "Coming soon!"),
138
+ t("editor.tools.effects"),
139
+ t("editor.tools.effectsComingSoon"),
140
140
  )
141
141
  }
142
142
  >
@@ -10,6 +10,7 @@ import {
10
10
  AtomicIcon,
11
11
  useAppDesignTokens,
12
12
  } from "@umituz/react-native-design-system";
13
+ import { useLocalization } from "@umituz/react-native-localization";
13
14
  import type { Layer, ImageLayer } from "../../domain/entities";
14
15
 
15
16
  export interface LayerActionsMenuProps {
@@ -38,6 +39,7 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
38
39
  onDelete,
39
40
  }) => {
40
41
  const tokens = useAppDesignTokens();
42
+ const { t } = useLocalization();
41
43
 
42
44
  return (
43
45
  <View style={{ paddingVertical: 8 }}>
@@ -51,7 +53,7 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
51
53
  marginLeft: 12,
52
54
  }}
53
55
  >
54
- Edit Text
56
+ {t("editor.layers.actions.editText")}
55
57
  </AtomicText>
56
58
  </TouchableOpacity>
57
59
  )}
@@ -65,7 +67,7 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
65
67
  marginLeft: 12,
66
68
  }}
67
69
  >
68
- Edit Image
70
+ {t("editor.layers.actions.editImage")}
69
71
  </AtomicText>
70
72
  </TouchableOpacity>
71
73
  )}
@@ -79,7 +81,9 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
79
81
  marginLeft: 12,
80
82
  }}
81
83
  >
82
- {layer.animation ? "Edit Animation" : "Add Animation"}
84
+ {layer.animation
85
+ ? t("editor.layers.actions.editAnimation")
86
+ : t("editor.layers.actions.addAnimation")}
83
87
  </AtomicText>
84
88
  {layer.animation && (
85
89
  <View
@@ -100,11 +104,13 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
100
104
  marginLeft: 12,
101
105
  }}
102
106
  >
103
- Duplicate Layer
107
+ {t("editor.layers.actions.duplicate")}
104
108
  </AtomicText>
105
109
  </TouchableOpacity>
106
110
 
107
- <View style={styles.divider} />
111
+ <View
112
+ style={[styles.divider, { backgroundColor: tokens.colors.border }]}
113
+ />
108
114
 
109
115
  <TouchableOpacity style={styles.actionMenuItem} onPress={onMoveFront}>
110
116
  <AtomicIcon name="chevron-up-outline" size="md" color="secondary" />
@@ -115,7 +121,7 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
115
121
  marginLeft: 12,
116
122
  }}
117
123
  >
118
- Bring to Front
124
+ {t("editor.layers.actions.bringToFront")}
119
125
  </AtomicText>
120
126
  </TouchableOpacity>
121
127
 
@@ -128,7 +134,7 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
128
134
  marginLeft: 12,
129
135
  }}
130
136
  >
131
- Move Up
137
+ {t("editor.layers.actions.moveUp")}
132
138
  </AtomicText>
133
139
  </TouchableOpacity>
134
140
 
@@ -141,7 +147,7 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
141
147
  marginLeft: 12,
142
148
  }}
143
149
  >
144
- Move Down
150
+ {t("editor.layers.actions.moveDown")}
145
151
  </AtomicText>
146
152
  </TouchableOpacity>
147
153
 
@@ -154,11 +160,13 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
154
160
  marginLeft: 12,
155
161
  }}
156
162
  >
157
- Send to Back
163
+ {t("editor.layers.actions.sendToBack")}
158
164
  </AtomicText>
159
165
  </TouchableOpacity>
160
166
 
161
- <View style={styles.divider} />
167
+ <View
168
+ style={[styles.divider, { backgroundColor: tokens.colors.border }]}
169
+ />
162
170
 
163
171
  <TouchableOpacity style={styles.actionMenuItem} onPress={onDelete}>
164
172
  <AtomicIcon name="trash-outline" size="md" color="error" />
@@ -169,7 +177,7 @@ export const LayerActionsMenu: React.FC<LayerActionsMenuProps> = ({
169
177
  marginLeft: 12,
170
178
  }}
171
179
  >
172
- Delete Layer
180
+ {t("editor.layers.actions.delete")}
173
181
  </AtomicText>
174
182
  </TouchableOpacity>
175
183
  </View>
@@ -185,7 +193,6 @@ const styles = StyleSheet.create({
185
193
  },
186
194
  divider: {
187
195
  height: 1,
188
- backgroundColor: "#E5E7EB",
189
196
  marginVertical: 8,
190
197
  },
191
198
  animationBadge: {
@@ -68,7 +68,7 @@ export const AnimationEditorActions: React.FC<AnimationEditorActionsProps> = ({
68
68
  <AtomicIcon name="checkmark-outline" size="sm" color="onSurface" />
69
69
  <AtomicText
70
70
  type="bodyMedium"
71
- style={{ color: "#FFFFFF", fontWeight: "600", marginLeft: 6 }}
71
+ style={{ color: tokens.colors.onPrimary, fontWeight: "600", marginLeft: 6 }}
72
72
  >
73
73
  Apply
74
74
  </AtomicText>
@@ -70,7 +70,7 @@ export const AnimationTypeSelector: React.FC<AnimationTypeSelectorProps> = ({
70
70
  style={{
71
71
  color:
72
72
  selectedType === anim.type
73
- ? "#FFFFFF"
73
+ ? tokens.colors.onPrimary
74
74
  : tokens.colors.textPrimary,
75
75
  marginTop: 6,
76
76
  fontWeight: selectedType === anim.type ? "600" : "400",
@@ -81,7 +81,7 @@ export const AudioEditorActions: React.FC<AudioEditorActionsProps> = ({
81
81
  <AtomicIcon name="checkmark-outline" size="sm" color="onSurface" />
82
82
  <AtomicText
83
83
  type="bodyMedium"
84
- style={{ color: "#FFFFFF", fontWeight: "600", marginLeft: 6 }}
84
+ style={{ color: tokens.colors.onPrimary, fontWeight: "600", marginLeft: 6 }}
85
85
  >
86
86
  Save
87
87
  </AtomicText>
@@ -68,7 +68,13 @@ export const AudioFileSelector: React.FC<AudioFileSelectorProps> = ({
68
68
 
69
69
  return (
70
70
  <TouchableOpacity
71
- style={[styles.pickButton, { backgroundColor: tokens.colors.surface }]}
71
+ style={[
72
+ styles.pickButton,
73
+ {
74
+ backgroundColor: tokens.colors.surface,
75
+ borderColor: tokens.colors.borderLight,
76
+ },
77
+ ]}
72
78
  onPress={onPickAudio}
73
79
  >
74
80
  <AtomicIcon name="cloud-upload-outline" size="md" color="primary" />
@@ -102,7 +108,6 @@ const styles = StyleSheet.create({
102
108
  alignItems: "center",
103
109
  justifyContent: "center",
104
110
  borderWidth: 2,
105
- borderColor: "rgba(0, 0, 0, 0.1)",
106
111
  borderStyle: "dashed",
107
112
  },
108
113
  fileCard: {
@@ -73,7 +73,7 @@ export const FadeEffectsSelector: React.FC<FadeEffectsSelectorProps> = ({
73
73
  type="labelSmall"
74
74
  style={{
75
75
  color:
76
- fadeIn === val ? "#FFFFFF" : tokens.colors.textPrimary,
76
+ fadeIn === val ? tokens.colors.onPrimary : tokens.colors.textPrimary,
77
77
  fontSize: 11,
78
78
  }}
79
79
  >
@@ -114,7 +114,7 @@ export const FadeEffectsSelector: React.FC<FadeEffectsSelectorProps> = ({
114
114
  type="labelSmall"
115
115
  style={{
116
116
  color:
117
- fadeOut === val ? "#FFFFFF" : tokens.colors.textPrimary,
117
+ fadeOut === val ? tokens.colors.onPrimary : tokens.colors.textPrimary,
118
118
  fontSize: 11,
119
119
  }}
120
120
  >
@@ -61,7 +61,7 @@ export const VolumeSelector: React.FC<VolumeSelectorProps> = ({
61
61
  <AtomicText
62
62
  type="labelSmall"
63
63
  style={{
64
- color: volume === val ? "#FFFFFF" : tokens.colors.textPrimary,
64
+ color: volume === val ? tokens.colors.onPrimary : tokens.colors.textPrimary,
65
65
  fontWeight: volume === val ? "600" : "400",
66
66
  }}
67
67
  >
@@ -51,7 +51,7 @@ export const LayerContent: React.FC<LayerContentProps> = ({ layer }) => {
51
51
  resizeMode="cover"
52
52
  />
53
53
  ) : (
54
- <View style={styles.imagePlaceholder}>
54
+ <View style={[styles.imagePlaceholder, { backgroundColor: tokens.colors.surface }]}>
55
55
  <AtomicIcon name="image-outline" size="md" color="secondary" />
56
56
  </View>
57
57
  );
@@ -95,7 +95,6 @@ const styles = StyleSheet.create({
95
95
  imagePlaceholder: {
96
96
  width: "100%",
97
97
  height: "100%",
98
- backgroundColor: "rgba(0,0,0,0.1)",
99
98
  alignItems: "center",
100
99
  justifyContent: "center",
101
100
  },
@@ -31,7 +31,10 @@ export const ResizeHandles: React.FC<ResizeHandlesProps> = ({
31
31
  style={[
32
32
  styles.handle,
33
33
  styles.handleTopLeft,
34
- { backgroundColor: tokens.colors.primary },
34
+ {
35
+ backgroundColor: tokens.colors.primary,
36
+ borderColor: tokens.colors.onPrimary,
37
+ },
35
38
  ]}
36
39
  />
37
40
  </GestureDetector>
@@ -41,7 +44,10 @@ export const ResizeHandles: React.FC<ResizeHandlesProps> = ({
41
44
  style={[
42
45
  styles.handle,
43
46
  styles.handleTopRight,
44
- { backgroundColor: tokens.colors.primary },
47
+ {
48
+ backgroundColor: tokens.colors.primary,
49
+ borderColor: tokens.colors.onPrimary,
50
+ },
45
51
  ]}
46
52
  />
47
53
  </GestureDetector>
@@ -51,7 +57,10 @@ export const ResizeHandles: React.FC<ResizeHandlesProps> = ({
51
57
  style={[
52
58
  styles.handle,
53
59
  styles.handleBottomLeft,
54
- { backgroundColor: tokens.colors.primary },
60
+ {
61
+ backgroundColor: tokens.colors.primary,
62
+ borderColor: tokens.colors.onPrimary,
63
+ },
55
64
  ]}
56
65
  />
57
66
  </GestureDetector>
@@ -61,7 +70,10 @@ export const ResizeHandles: React.FC<ResizeHandlesProps> = ({
61
70
  style={[
62
71
  styles.handle,
63
72
  styles.handleBottomRight,
64
- { backgroundColor: tokens.colors.primary },
73
+ {
74
+ backgroundColor: tokens.colors.primary,
75
+ borderColor: tokens.colors.onPrimary,
76
+ },
65
77
  ]}
66
78
  />
67
79
  </GestureDetector>
@@ -76,7 +88,6 @@ const styles = StyleSheet.create({
76
88
  height: 12,
77
89
  borderRadius: 6,
78
90
  borderWidth: 2,
79
- borderColor: "#FFFFFF",
80
91
  },
81
92
  handleTopLeft: {
82
93
  top: -6,
@@ -70,7 +70,7 @@ export const ExportActions: React.FC<ExportActionsProps> = ({
70
70
  )}
71
71
  <AtomicText
72
72
  type="bodyMedium"
73
- style={{ color: "#FFFFFF", fontWeight: "600", marginLeft: 8 }}
73
+ style={{ color: tokens.colors.onPrimary, fontWeight: "600", marginLeft: 8 }}
74
74
  >
75
75
  {isExporting ? "Exporting..." : "Export Video"}
76
76
  </AtomicText>
@@ -57,7 +57,12 @@ export const ExportProgress: React.FC<ExportProgressProps> = ({ progress }) => {
57
57
  </AtomicText>
58
58
  </View>
59
59
 
60
- <View style={styles.progressBarContainer}>
60
+ <View
61
+ style={[
62
+ styles.progressBarContainer,
63
+ { backgroundColor: tokens.colors.borderLight },
64
+ ]}
65
+ >
61
66
  <View
62
67
  style={[
63
68
  styles.progressBar,
@@ -98,7 +103,6 @@ const styles = StyleSheet.create({
98
103
  },
99
104
  progressBarContainer: {
100
105
  height: 8,
101
- backgroundColor: "rgba(0, 0, 0, 0.1)",
102
106
  borderRadius: 4,
103
107
  overflow: "hidden",
104
108
  marginBottom: 8,
@@ -67,7 +67,7 @@ export function OptionSelectorRow<T extends string>({
67
67
  style={{
68
68
  color:
69
69
  selectedValue === option.value
70
- ? "#FFFFFF"
70
+ ? tokens.colors.onPrimary
71
71
  : tokens.colors.textPrimary,
72
72
  fontWeight: selectedValue === option.value ? "600" : "400",
73
73
  textTransform: option.textTransform || "none",
@@ -54,7 +54,7 @@ export const WatermarkToggle: React.FC<WatermarkToggleProps> = ({
54
54
  style={[
55
55
  styles.toggleThumb,
56
56
  {
57
- backgroundColor: "#FFFFFF",
57
+ backgroundColor: tokens.colors.onPrimary,
58
58
  transform: [{ translateX: includeWatermark ? 20 : 0 }],
59
59
  },
60
60
  ]}
@@ -59,7 +59,7 @@ export const OpacitySelector: React.FC<OpacitySelectorProps> = ({
59
59
  type="bodySmall"
60
60
  style={{
61
61
  color:
62
- opacity === value ? "#FFFFFF" : tokens.colors.textPrimary,
62
+ opacity === value ? tokens.colors.onPrimary : tokens.colors.textPrimary,
63
63
  fontWeight: opacity === value ? "600" : "400",
64
64
  }}
65
65
  >
@@ -53,7 +53,7 @@ export const ColorPickerHorizontal: React.FC<ColorPickerHorizontalProps> = ({
53
53
  borderColor:
54
54
  selectedColor === color.value
55
55
  ? tokens.colors.primary
56
- : "#E5E7EB",
56
+ : tokens.colors.borderLight,
57
57
  borderWidth: selectedColor === color.value ? 3 : 2,
58
58
  },
59
59
  ]}
@@ -68,7 +68,7 @@ export const ShapeTypeSelector: React.FC<ShapeTypeSelectorProps> = ({
68
68
  style={{
69
69
  color:
70
70
  selectedShape === s.type
71
- ? "#FFFFFF"
71
+ ? tokens.colors.onPrimary
72
72
  : tokens.colors.textPrimary,
73
73
  marginTop: 8,
74
74
  fontWeight: selectedShape === s.type ? "600" : "400",
@@ -66,7 +66,7 @@ export const ValueSelector: React.FC<ValueSelectorProps> = ({
66
66
  <AtomicText
67
67
  type="labelSmall"
68
68
  style={{
69
- color: value === option ? "#FFFFFF" : tokens.colors.textPrimary,
69
+ color: value === option ? tokens.colors.onPrimary : tokens.colors.textPrimary,
70
70
  fontWeight: value === option ? "600" : "400",
71
71
  }}
72
72
  >
@@ -44,7 +44,7 @@ export const EditorActions: React.FC<EditorActionsProps> = ({
44
44
  type="bodyMedium"
45
45
  style={{ color: tokens.colors.textSecondary }}
46
46
  >
47
- {t("common.buttons.cancel", "Cancel")}
47
+ {t("common.buttons.cancel")}
48
48
  </AtomicText>
49
49
  </TouchableOpacity>
50
50
 
@@ -59,7 +59,7 @@ export const FontSizeSelector: React.FC<FontSizeSelectorProps> = ({
59
59
  type="bodySmall"
60
60
  style={{
61
61
  color:
62
- fontSize === size ? "#FFFFFF" : tokens.colors.textPrimary,
62
+ fontSize === size ? tokens.colors.onPrimary : tokens.colors.textPrimary,
63
63
  fontWeight: fontSize === size ? "600" : "400",
64
64
  }}
65
65
  >
@@ -5,11 +5,12 @@
5
5
 
6
6
  import { useCallback } from "react";
7
7
  import { Alert } from "react-native";
8
+ import { useLocalization } from "@umituz/react-native-localization";
8
9
  // TODO: Refactor to use TanStack Query instead of store
9
10
  // Temporary stub until refactor
10
11
  const useHistoryStore = () => ({
11
12
  addToHistory: () => {},
12
- pushHistory: (_project: any, _action: string) => {},
13
+ pushHistory: (_project: VideoProject | undefined, _action: string) => {},
13
14
  undo: () => undefined,
14
15
  redo: () => undefined,
15
16
  canUndo: () => false,
@@ -36,6 +37,7 @@ export function useEditorHistory({
36
37
  projectId,
37
38
  onUpdateProject,
38
39
  }: UseEditorHistoryParams): UseEditorHistoryReturn {
40
+ const { t } = useLocalization();
39
41
  const {
40
42
  pushHistory,
41
43
  undo: historyUndo,
@@ -58,17 +60,17 @@ export function useEditorHistory({
58
60
  const previousState = historyUndo();
59
61
  if (previousState) {
60
62
  onUpdateProject(previousState);
61
- Alert.alert("Undo", "Action undone");
63
+ Alert.alert(t("editor.history.undo.success"));
62
64
  }
63
- }, [historyUndo, onUpdateProject]);
65
+ }, [historyUndo, onUpdateProject, t]);
64
66
 
65
67
  const redo = useCallback(() => {
66
68
  const nextState = historyRedo();
67
69
  if (nextState) {
68
70
  onUpdateProject(nextState);
69
- Alert.alert("Redo", "Action redone");
71
+ Alert.alert(t("editor.history.redo.success"));
70
72
  }
71
- }, [historyRedo, onUpdateProject]);
73
+ }, [historyRedo, onUpdateProject, t]);
72
74
 
73
75
  return {
74
76
  undo,
@@ -5,13 +5,14 @@
5
5
 
6
6
  import { useCallback } from "react";
7
7
  import { Alert } from "react-native";
8
+ import { useLocalization } from "@umituz/react-native-localization";
8
9
  import { sceneOperationsService } from "../../infrastructure/services/scene-operations.service";
9
- import type { Audio } from "../../domain/entities";
10
+ import type { Scene, Audio } from "../../domain/entities";
10
11
 
11
12
  export interface UseEditorScenesParams {
12
- scenes: any[];
13
+ scenes: Scene[];
13
14
  currentSceneIndex: number;
14
- onUpdateScenes: (scenes: any[]) => void;
15
+ onUpdateScenes: (scenes: Scene[]) => void;
15
16
  onSceneIndexChange: (index: number) => void;
16
17
  }
17
18
 
@@ -28,6 +29,8 @@ export function useEditorScenes({
28
29
  onUpdateScenes,
29
30
  onSceneIndexChange,
30
31
  }: UseEditorScenesParams): UseEditorScenesReturn {
32
+ const { t } = useLocalization();
33
+
31
34
  const addScene = useCallback(() => {
32
35
  const result = sceneOperationsService.addScene(scenes);
33
36
  if (result.success) {
@@ -35,11 +38,11 @@ export function useEditorScenes({
35
38
  if (result.newSceneIndex !== undefined) {
36
39
  onSceneIndexChange(result.newSceneIndex);
37
40
  }
38
- Alert.alert("Success", "New scene added!");
41
+ Alert.alert(t("editor.scenes.add.success"));
39
42
  } else {
40
- Alert.alert("Error", result.error || "Failed to add scene");
43
+ Alert.alert(t("editor.scenes.add.error"));
41
44
  }
42
- }, [scenes, onUpdateScenes, onSceneIndexChange]);
45
+ }, [scenes, onUpdateScenes, onSceneIndexChange, t]);
43
46
 
44
47
  const duplicateScene = useCallback(
45
48
  (sceneIndex: number) => {
@@ -49,12 +52,12 @@ export function useEditorScenes({
49
52
  if (result.newSceneIndex !== undefined) {
50
53
  onSceneIndexChange(result.newSceneIndex);
51
54
  }
52
- Alert.alert("Success", "Scene duplicated!");
55
+ Alert.alert(t("editor.scenes.duplicate.success"));
53
56
  } else {
54
- Alert.alert("Error", result.error || "Failed to duplicate scene");
57
+ Alert.alert(t("editor.scenes.duplicate.error"));
55
58
  }
56
59
  },
57
- [scenes, onUpdateScenes, onSceneIndexChange],
60
+ [scenes, onUpdateScenes, onSceneIndexChange, t],
58
61
  );
59
62
 
60
63
  const deleteScene = useCallback(
@@ -69,12 +72,12 @@ export function useEditorScenes({
69
72
  if (result.newSceneIndex !== undefined) {
70
73
  onSceneIndexChange(result.newSceneIndex);
71
74
  }
72
- Alert.alert("Success", "Scene deleted");
75
+ Alert.alert(t("editor.scenes.delete.success"));
73
76
  } else {
74
- Alert.alert("Error", result.error || "Failed to delete scene");
77
+ Alert.alert(t("editor.scenes.delete.error"));
75
78
  }
76
79
  },
77
- [scenes, currentSceneIndex, onUpdateScenes, onSceneIndexChange],
80
+ [scenes, currentSceneIndex, onUpdateScenes, onSceneIndexChange, t],
78
81
  );
79
82
 
80
83
  const updateSceneAudio = useCallback(
@@ -87,14 +90,14 @@ export function useEditorScenes({
87
90
  if (result.success) {
88
91
  onUpdateScenes(result.updatedScenes);
89
92
  Alert.alert(
90
- "Success",
91
- audio ? "Audio added to scene!" : "Audio removed from scene",
93
+ t("editor.scenes.audio.success"),
94
+ t(audio ? "editor.scenes.audio.added" : "editor.scenes.audio.removed"),
92
95
  );
93
96
  } else {
94
- Alert.alert("Error", result.error || "Failed to update scene audio");
97
+ Alert.alert(t("editor.scenes.audio.error"));
95
98
  }
96
99
  },
97
- [scenes, currentSceneIndex, onUpdateScenes],
100
+ [scenes, currentSceneIndex, onUpdateScenes, t],
98
101
  );
99
102
 
100
103
  return {
@@ -5,14 +5,14 @@
5
5
 
6
6
  import { useCallback } from "react";
7
7
  import { Alert } from "react-native";
8
+ import { useLocalization } from "@umituz/react-native-localization";
8
9
  import { layerOperationsService } from "../../infrastructure/services/layer-operations.service";
9
- import type { AddImageLayerData } from "../../domain/entities";
10
- import type { ImageLayer } from "../../domain/entities";
10
+ import type { AddImageLayerData, Scene, ImageLayer } from "../../domain/entities";
11
11
 
12
12
  export interface UseImageLayerOperationsParams {
13
- scenes: any[];
13
+ scenes: Scene[];
14
14
  sceneIndex: number;
15
- onUpdateScenes: (scenes: any[]) => void;
15
+ onUpdateScenes: (scenes: Scene[]) => void;
16
16
  onCloseBottomSheet: () => void;
17
17
  }
18
18
 
@@ -27,6 +27,8 @@ export function useImageLayerOperations({
27
27
  onUpdateScenes,
28
28
  onCloseBottomSheet,
29
29
  }: UseImageLayerOperationsParams): UseImageLayerOperationsReturn {
30
+ const { t } = useLocalization();
31
+
30
32
  const addImageLayer = useCallback(
31
33
  (data: AddImageLayerData) => {
32
34
  const result = layerOperationsService.addImageLayer(
@@ -37,12 +39,12 @@ export function useImageLayerOperations({
37
39
  if (result.success) {
38
40
  onUpdateScenes(result.updatedScenes);
39
41
  onCloseBottomSheet();
40
- Alert.alert("Success", "Image layer added!");
42
+ Alert.alert(t("editor.layers.image.add.success"));
41
43
  } else {
42
- Alert.alert("Error", result.error || "Failed to add image layer");
44
+ Alert.alert(t("editor.layers.image.add.error"));
43
45
  }
44
46
  },
45
- [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet],
47
+ [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet, t],
46
48
  );
47
49
 
48
50
  const editImageLayer = useCallback(
@@ -56,12 +58,12 @@ export function useImageLayerOperations({
56
58
  if (result.success) {
57
59
  onUpdateScenes(result.updatedScenes);
58
60
  onCloseBottomSheet();
59
- Alert.alert("Success", "Image layer updated!");
61
+ Alert.alert(t("editor.layers.image.update.success"));
60
62
  } else {
61
- Alert.alert("Error", result.error || "Failed to update image layer");
63
+ Alert.alert(t("editor.layers.image.update.error"));
62
64
  }
63
65
  },
64
- [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet],
66
+ [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet, t],
65
67
  );
66
68
 
67
69
  return {
@@ -4,17 +4,18 @@
4
4
  */
5
5
 
6
6
  import { useCallback } from "react";
7
+ import { useLocalization } from "@umituz/react-native-localization";
7
8
  import { TextLayerEditor } from "../components/TextLayerEditor";
8
9
  import { ImageLayerEditor } from "../components/ImageLayerEditor";
9
10
  import { ShapeLayerEditor } from "../components/ShapeLayerEditor";
10
11
  import { AnimationEditor } from "../components/AnimationEditor";
11
- import type { ImageLayer } from "../../domain/entities";
12
+ import type { Scene, ImageLayer, Layer } from "../../domain/entities";
12
13
  import type { UseEditorLayersReturn } from "./useEditorLayers";
13
14
  import type { UseEditorBottomSheetReturn } from "./useEditorBottomSheet";
14
15
 
15
16
  export interface UseLayerActionsParams {
16
17
  selectedLayerId: string | null;
17
- currentScene: any;
18
+ currentScene: Scene | undefined;
18
19
  layers: UseEditorLayersReturn;
19
20
  bottomSheet: UseEditorBottomSheetReturn;
20
21
  }
@@ -34,11 +35,12 @@ export function useLayerActions({
34
35
  layers,
35
36
  bottomSheet,
36
37
  }: UseLayerActionsParams): UseLayerActionsReturn {
38
+ const { t } = useLocalization();
37
39
  const { openBottomSheet, closeBottomSheet } = bottomSheet;
38
40
 
39
41
  const handleAddText = useCallback(() => {
40
42
  openBottomSheet({
41
- title: "Add Text Layer",
43
+ title: t("editor.layers.text.add"),
42
44
  children: (
43
45
  <TextLayerEditor
44
46
  onSave={layers.addTextLayer}
@@ -46,17 +48,17 @@ export function useLayerActions({
46
48
  />
47
49
  ),
48
50
  });
49
- }, [layers.addTextLayer, openBottomSheet, closeBottomSheet]);
51
+ }, [layers.addTextLayer, openBottomSheet, closeBottomSheet, t]);
50
52
 
51
53
  const handleEditLayer = useCallback(() => {
52
54
  if (!selectedLayerId || !currentScene) return;
53
55
  const layer = currentScene.layers.find(
54
- (l: any) => l.id === selectedLayerId,
56
+ (l: Layer) => l.id === selectedLayerId,
55
57
  );
56
58
  if (!layer || layer.type !== "text") return;
57
59
 
58
60
  openBottomSheet({
59
- title: "Edit Text Layer",
61
+ title: t("editor.layers.text.edit"),
60
62
  children: (
61
63
  <TextLayerEditor
62
64
  layer={layer}
@@ -71,11 +73,12 @@ export function useLayerActions({
71
73
  layers.editTextLayer,
72
74
  openBottomSheet,
73
75
  closeBottomSheet,
76
+ t,
74
77
  ]);
75
78
 
76
79
  const handleAddImage = useCallback(() => {
77
80
  openBottomSheet({
78
- title: "Add Image Layer",
81
+ title: t("editor.layers.image.add"),
79
82
  children: (
80
83
  <ImageLayerEditor
81
84
  onSave={layers.addImageLayer}
@@ -83,18 +86,18 @@ export function useLayerActions({
83
86
  />
84
87
  ),
85
88
  });
86
- }, [layers.addImageLayer, openBottomSheet, closeBottomSheet]);
89
+ }, [layers.addImageLayer, openBottomSheet, closeBottomSheet, t]);
87
90
 
88
91
  const handleEditImageLayer = useCallback(
89
92
  (layerId: string) => {
90
93
  if (!currentScene) return;
91
- const layer = currentScene.layers.find((l: any) => l.id === layerId) as
94
+ const layer = currentScene.layers.find((l: Layer) => l.id === layerId) as
92
95
  | ImageLayer
93
96
  | undefined;
94
97
  if (!layer) return;
95
98
 
96
99
  openBottomSheet({
97
- title: "Edit Image Layer",
100
+ title: t("editor.layers.image.edit"),
98
101
  children: (
99
102
  <ImageLayerEditor
100
103
  layer={layer}
@@ -104,12 +107,12 @@ export function useLayerActions({
104
107
  ),
105
108
  });
106
109
  },
107
- [currentScene, layers.editImageLayer, openBottomSheet, closeBottomSheet],
110
+ [currentScene, layers.editImageLayer, openBottomSheet, closeBottomSheet, t],
108
111
  );
109
112
 
110
113
  const handleAddShape = useCallback(() => {
111
114
  openBottomSheet({
112
- title: "Add Shape Layer",
115
+ title: t("editor.layers.shape.add"),
113
116
  children: (
114
117
  <ShapeLayerEditor
115
118
  onSave={layers.addShapeLayer}
@@ -117,16 +120,18 @@ export function useLayerActions({
117
120
  />
118
121
  ),
119
122
  });
120
- }, [layers.addShapeLayer, openBottomSheet, closeBottomSheet]);
123
+ }, [layers.addShapeLayer, openBottomSheet, closeBottomSheet, t]);
121
124
 
122
125
  const handleAnimate = useCallback(
123
126
  (layerId: string) => {
124
127
  if (!currentScene) return;
125
- const layer = currentScene.layers.find((l: any) => l.id === layerId);
128
+ const layer = currentScene.layers.find((l: Layer) => l.id === layerId);
126
129
  if (!layer) return;
127
130
 
128
131
  openBottomSheet({
129
- title: layer.animation ? "Edit Animation" : "Add Animation",
132
+ title: layer.animation
133
+ ? t("editor.layers.animation.edit")
134
+ : t("editor.layers.animation.add"),
130
135
  children: (
131
136
  <AnimationEditor
132
137
  animation={layer.animation}
@@ -148,6 +153,7 @@ export function useLayerActions({
148
153
  layers.updateLayerAnimation,
149
154
  openBottomSheet,
150
155
  closeBottomSheet,
156
+ t,
151
157
  ],
152
158
  );
153
159
 
@@ -5,14 +5,14 @@
5
5
 
6
6
  import { useCallback } from "react";
7
7
  import { Alert } from "react-native";
8
+ import { useLocalization } from "@umituz/react-native-localization";
8
9
  import { layerOperationsService } from "../../infrastructure/services/layer-operations.service";
9
- import type { LayerOrderAction } from "../../domain/entities";
10
- import type { Animation } from "../../domain/entities";
10
+ import type { Scene, LayerOrderAction, Animation } from "../../domain/entities";
11
11
 
12
12
  export interface UseLayerManipulationParams {
13
- scenes: any[];
13
+ scenes: Scene[];
14
14
  sceneIndex: number;
15
- onUpdateScenes: (scenes: any[]) => void;
15
+ onUpdateScenes: (scenes: Scene[]) => void;
16
16
  onCloseBottomSheet: () => void;
17
17
  onLayerDeleted?: () => void;
18
18
  }
@@ -36,15 +36,17 @@ export function useLayerManipulation({
36
36
  onCloseBottomSheet,
37
37
  onLayerDeleted,
38
38
  }: UseLayerManipulationParams): UseLayerManipulationReturn {
39
+ const { t } = useLocalization();
40
+
39
41
  const deleteLayer = useCallback(
40
42
  (layerId: string) => {
41
43
  Alert.alert(
42
- "Delete Layer",
43
- "Are you sure you want to delete this layer?",
44
+ t("editor.layers.delete.title"),
45
+ t("editor.layers.delete.message"),
44
46
  [
45
- { text: "Cancel", style: "cancel" },
47
+ { text: t("common.buttons.cancel"), style: "cancel" },
46
48
  {
47
- text: "Delete",
49
+ text: t("editor.layers.delete.confirm"),
48
50
  style: "destructive",
49
51
  onPress: () => {
50
52
  const result = layerOperationsService.deleteLayer(
@@ -55,16 +57,16 @@ export function useLayerManipulation({
55
57
  if (result.success) {
56
58
  onUpdateScenes(result.updatedScenes);
57
59
  onLayerDeleted?.();
58
- Alert.alert("Success", "Layer deleted");
60
+ Alert.alert(t("editor.layers.delete.success"));
59
61
  } else {
60
- Alert.alert("Error", result.error || "Failed to delete layer");
62
+ Alert.alert(t("editor.layers.delete.error"));
61
63
  }
62
64
  },
63
65
  },
64
66
  ],
65
67
  );
66
68
  },
67
- [scenes, sceneIndex, onUpdateScenes, onLayerDeleted],
69
+ [scenes, sceneIndex, onUpdateScenes, onLayerDeleted, t],
68
70
  );
69
71
 
70
72
  const changeLayerOrder = useCallback(
@@ -77,18 +79,12 @@ export function useLayerManipulation({
77
79
  );
78
80
  if (result.success) {
79
81
  onUpdateScenes(result.updatedScenes);
80
- const actionNames = {
81
- front: "Layer moved to front",
82
- back: "Layer moved to back",
83
- up: "Layer moved up",
84
- down: "Layer moved down",
85
- };
86
- Alert.alert("Success", actionNames[action]);
82
+ Alert.alert(t("editor.layers.order.success"), t(`editor.layers.order.${action}`));
87
83
  } else {
88
- Alert.alert("Error", result.error || "Failed to change layer order");
84
+ Alert.alert(t("editor.layers.order.error"));
89
85
  }
90
86
  },
91
- [scenes, sceneIndex, onUpdateScenes],
87
+ [scenes, sceneIndex, onUpdateScenes, t],
92
88
  );
93
89
 
94
90
  const duplicateLayer = useCallback(
@@ -100,12 +96,12 @@ export function useLayerManipulation({
100
96
  );
101
97
  if (result.success) {
102
98
  onUpdateScenes(result.updatedScenes);
103
- Alert.alert("Success", "Layer duplicated!");
99
+ Alert.alert(t("editor.layers.duplicate.success"));
104
100
  } else {
105
- Alert.alert("Error", result.error || "Failed to duplicate layer");
101
+ Alert.alert(t("editor.layers.duplicate.error"));
106
102
  }
107
103
  },
108
- [scenes, sceneIndex, onUpdateScenes],
104
+ [scenes, sceneIndex, onUpdateScenes, t],
109
105
  );
110
106
 
111
107
  const updateLayerPosition = useCallback(
@@ -152,19 +148,16 @@ export function useLayerManipulation({
152
148
  onUpdateScenes(result.updatedScenes);
153
149
  onCloseBottomSheet();
154
150
  Alert.alert(
155
- "Success",
156
- animation
157
- ? "Animation applied to layer!"
158
- : "Animation removed from layer",
151
+ t("editor.layers.animation.success"),
152
+ t(animation
153
+ ? "editor.layers.animation.applied"
154
+ : "editor.layers.animation.removed"),
159
155
  );
160
156
  } else {
161
- Alert.alert(
162
- "Error",
163
- result.error || "Failed to update layer animation",
164
- );
157
+ Alert.alert(t("editor.layers.animation.error"));
165
158
  }
166
159
  },
167
- [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet],
160
+ [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet, t],
168
161
  );
169
162
 
170
163
  return {
@@ -36,7 +36,7 @@ export function useShapeLayerForm(
36
36
 
37
37
  const [formState, setFormState] = useState<ShapeLayerFormState>({
38
38
  shape: initialLayer?.shape || "rectangle",
39
- fillColor: initialLayer?.fillColor || "#3B82F6",
39
+ fillColor: initialLayer?.fillColor || tokens.colors.primary,
40
40
  borderColor: initialLayer?.borderColor || tokens.colors.textPrimary,
41
41
  borderWidth: initialLayer?.borderWidth || 0,
42
42
  opacity: initialLayer?.opacity || 1,
@@ -5,13 +5,14 @@
5
5
 
6
6
  import { useCallback } from "react";
7
7
  import { Alert } from "react-native";
8
+ import { useLocalization } from "@umituz/react-native-localization";
8
9
  import { layerOperationsService } from "../../infrastructure/services/layer-operations.service";
9
- import type { AddShapeLayerData } from "../../domain/entities";
10
+ import type { AddShapeLayerData, Scene } from "../../domain/entities";
10
11
 
11
12
  export interface UseShapeLayerOperationsParams {
12
- scenes: any[];
13
+ scenes: Scene[];
13
14
  sceneIndex: number;
14
- onUpdateScenes: (scenes: any[]) => void;
15
+ onUpdateScenes: (scenes: Scene[]) => void;
15
16
  onCloseBottomSheet: () => void;
16
17
  defaultColor: string;
17
18
  }
@@ -27,6 +28,8 @@ export function useShapeLayerOperations({
27
28
  onCloseBottomSheet,
28
29
  defaultColor,
29
30
  }: UseShapeLayerOperationsParams): UseShapeLayerOperationsReturn {
31
+ const { t } = useLocalization();
32
+
30
33
  const addShapeLayer = useCallback(
31
34
  (data: AddShapeLayerData) => {
32
35
  const result = layerOperationsService.addShapeLayer(
@@ -38,12 +41,12 @@ export function useShapeLayerOperations({
38
41
  if (result.success) {
39
42
  onUpdateScenes(result.updatedScenes);
40
43
  onCloseBottomSheet();
41
- Alert.alert("Success", "Shape layer added!");
44
+ Alert.alert(t("editor.layers.shape.add.success"));
42
45
  } else {
43
- Alert.alert("Error", result.error || "Failed to add shape layer");
46
+ Alert.alert(t("editor.layers.shape.add.error"));
44
47
  }
45
48
  },
46
- [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet, defaultColor],
49
+ [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet, defaultColor, t],
47
50
  );
48
51
 
49
52
  return {
@@ -5,14 +5,14 @@
5
5
 
6
6
  import { useCallback } from "react";
7
7
  import { Alert } from "react-native";
8
+ import { useLocalization } from "@umituz/react-native-localization";
8
9
  import { layerOperationsService } from "../../infrastructure/services/layer-operations.service";
9
- import type { AddTextLayerData } from "../../domain/entities";
10
- import type { TextLayer } from "../../domain/entities";
10
+ import type { AddTextLayerData, Scene, TextLayer } from "../../domain/entities";
11
11
 
12
12
  export interface UseTextLayerOperationsParams {
13
- scenes: any[];
13
+ scenes: Scene[];
14
14
  sceneIndex: number;
15
- onUpdateScenes: (scenes: any[]) => void;
15
+ onUpdateScenes: (scenes: Scene[]) => void;
16
16
  onCloseBottomSheet: () => void;
17
17
  defaultColor: string;
18
18
  }
@@ -29,6 +29,8 @@ export function useTextLayerOperations({
29
29
  onCloseBottomSheet,
30
30
  defaultColor,
31
31
  }: UseTextLayerOperationsParams): UseTextLayerOperationsReturn {
32
+ const { t } = useLocalization();
33
+
32
34
  const addTextLayer = useCallback(
33
35
  (data: AddTextLayerData) => {
34
36
  const result = layerOperationsService.addTextLayer(
@@ -40,12 +42,12 @@ export function useTextLayerOperations({
40
42
  if (result.success) {
41
43
  onUpdateScenes(result.updatedScenes);
42
44
  onCloseBottomSheet();
43
- Alert.alert("Success", "Text layer added!");
45
+ Alert.alert(t("editor.layers.text.add.success"));
44
46
  } else {
45
- Alert.alert("Error", result.error || "Failed to add text layer");
47
+ Alert.alert(t("editor.layers.text.add.error"));
46
48
  }
47
49
  },
48
- [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet, defaultColor],
50
+ [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet, defaultColor, t],
49
51
  );
50
52
 
51
53
  const editTextLayer = useCallback(
@@ -59,12 +61,12 @@ export function useTextLayerOperations({
59
61
  if (result.success) {
60
62
  onUpdateScenes(result.updatedScenes);
61
63
  onCloseBottomSheet();
62
- Alert.alert("Success", "Text layer updated!");
64
+ Alert.alert(t("editor.layers.text.update.success"));
63
65
  } else {
64
- Alert.alert("Error", result.error || "Failed to update text layer");
66
+ Alert.alert(t("editor.layers.text.update.error"));
65
67
  }
66
68
  },
67
- [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet],
69
+ [scenes, sceneIndex, onUpdateScenes, onCloseBottomSheet, t],
68
70
  );
69
71
 
70
72
  return {