@umituz/react-native-ai-generation-content 1.17.6 → 1.17.8

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 (25) hide show
  1. package/package.json +1 -1
  2. package/src/features/ai-hug/index.ts +4 -0
  3. package/src/features/ai-hug/presentation/components/AIHugFeature.tsx +209 -0
  4. package/src/features/ai-hug/presentation/components/index.ts +2 -0
  5. package/src/features/ai-hug/presentation/index.ts +1 -0
  6. package/src/features/ai-kiss/index.ts +4 -0
  7. package/src/features/ai-kiss/presentation/components/AIKissFeature.tsx +202 -0
  8. package/src/features/ai-kiss/presentation/components/index.ts +2 -0
  9. package/src/features/ai-kiss/presentation/index.ts +1 -0
  10. package/src/features/anime-selfie/index.ts +4 -0
  11. package/src/features/anime-selfie/presentation/components/AnimeSelfieFeature.tsx +199 -0
  12. package/src/features/anime-selfie/presentation/components/index.ts +2 -0
  13. package/src/features/anime-selfie/presentation/index.ts +1 -0
  14. package/src/features/face-swap/index.ts +4 -0
  15. package/src/features/face-swap/presentation/components/FaceSwapFeature.tsx +202 -0
  16. package/src/features/face-swap/presentation/components/index.ts +2 -0
  17. package/src/features/face-swap/presentation/index.ts +1 -0
  18. package/src/features/photo-restoration/presentation/components/PhotoRestoreFeature.tsx +48 -36
  19. package/src/features/remove-background/index.ts +4 -0
  20. package/src/features/remove-background/presentation/components/RemoveBackgroundFeature.tsx +199 -0
  21. package/src/features/remove-background/presentation/components/index.ts +2 -0
  22. package/src/features/remove-background/presentation/index.ts +1 -0
  23. package/src/features/upscaling/presentation/components/UpscaleFeature.tsx +48 -36
  24. package/src/presentation/components/photo-step/PhotoStep.tsx +2 -4
  25. package/src/presentation/types/flow-config.types.ts +1 -1
@@ -0,0 +1,202 @@
1
+ /**
2
+ * FaceSwapFeature Component
3
+ * Self-contained face swap feature UI component
4
+ * Uses hook internally, only requires config and translations
5
+ */
6
+
7
+ import React, { useCallback } from "react";
8
+ import { View, ScrollView, StyleSheet, Image, Dimensions } from "react-native";
9
+ import {
10
+ useAppDesignTokens,
11
+ AtomicText,
12
+ AtomicButton,
13
+ } from "@umituz/react-native-design-system";
14
+ import { DualImagePicker } from "../../../../presentation/components/image-picker/DualImagePicker";
15
+ import { ErrorDisplay } from "../../../../presentation/components/display/ErrorDisplay";
16
+ import { useFaceSwapFeature } from "../hooks";
17
+ import type {
18
+ FaceSwapTranslations,
19
+ FaceSwapFeatureConfig,
20
+ } from "../../domain/types";
21
+
22
+ export interface FaceSwapFeatureProps {
23
+ config: FaceSwapFeatureConfig;
24
+ userId: string;
25
+ translations: FaceSwapTranslations;
26
+ onSelectSourceImage: () => Promise<string | null>;
27
+ onSelectTargetImage: () => Promise<string | null>;
28
+ onSaveImage: (imageUrl: string) => Promise<void>;
29
+ renderProcessingModal?: (props: {
30
+ visible: boolean;
31
+ progress: number;
32
+ }) => React.ReactNode;
33
+ }
34
+
35
+ export const FaceSwapFeature: React.FC<FaceSwapFeatureProps> = ({
36
+ config,
37
+ userId,
38
+ translations,
39
+ onSelectSourceImage,
40
+ onSelectTargetImage,
41
+ onSaveImage,
42
+ renderProcessingModal,
43
+ }) => {
44
+ const tokens = useAppDesignTokens();
45
+
46
+ const feature = useFaceSwapFeature({
47
+ config,
48
+ userId,
49
+ onSelectSourceImage,
50
+ onSelectTargetImage,
51
+ onSaveImage,
52
+ });
53
+
54
+ const handleProcess = useCallback(() => {
55
+ void feature.process();
56
+ }, [feature]);
57
+
58
+ const handleSave = useCallback(() => {
59
+ void feature.save();
60
+ }, [feature]);
61
+
62
+ const handleSelectSource = useCallback(() => {
63
+ void feature.selectSourceImage();
64
+ }, [feature]);
65
+
66
+ const handleSelectTarget = useCallback(() => {
67
+ void feature.selectTargetImage();
68
+ }, [feature]);
69
+
70
+ const canProcess = feature.sourceImageUri && feature.targetImageUri && !feature.isProcessing;
71
+
72
+ if (feature.processedUrl) {
73
+ const screenWidth = Dimensions.get("window").width;
74
+ const imageSize = screenWidth - 48;
75
+
76
+ return (
77
+ <ScrollView
78
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
79
+ contentContainerStyle={styles.content}
80
+ showsVerticalScrollIndicator={false}
81
+ >
82
+ <AtomicText
83
+ type="headlineMedium"
84
+ style={[styles.successText, { color: tokens.colors.success }]}
85
+ >
86
+ {translations.successText}
87
+ </AtomicText>
88
+
89
+ <View style={styles.resultImageContainer}>
90
+ <Image
91
+ source={{ uri: feature.processedUrl }}
92
+ style={[styles.resultImage, { width: imageSize, height: imageSize }]}
93
+ resizeMode="contain"
94
+ />
95
+ </View>
96
+
97
+ <View style={styles.resultActions}>
98
+ <AtomicButton
99
+ title={translations.saveButtonText}
100
+ onPress={handleSave}
101
+ variant="primary"
102
+ size="lg"
103
+ />
104
+ <AtomicButton
105
+ title={translations.tryAnotherText}
106
+ onPress={feature.reset}
107
+ variant="secondary"
108
+ size="lg"
109
+ />
110
+ </View>
111
+ </ScrollView>
112
+ );
113
+ }
114
+
115
+ return (
116
+ <>
117
+ <ScrollView
118
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
119
+ contentContainerStyle={styles.content}
120
+ showsVerticalScrollIndicator={false}
121
+ >
122
+ <AtomicText
123
+ type="bodyLarge"
124
+ style={[styles.description, { color: tokens.colors.textSecondary }]}
125
+ >
126
+ {translations.description}
127
+ </AtomicText>
128
+
129
+ <View style={styles.pickerContainer}>
130
+ <DualImagePicker
131
+ sourceImageUri={feature.sourceImageUri}
132
+ targetImageUri={feature.targetImageUri}
133
+ isDisabled={feature.isProcessing}
134
+ onSelectSource={handleSelectSource}
135
+ onSelectTarget={handleSelectTarget}
136
+ sourcePlaceholder={translations.sourceUploadTitle}
137
+ targetPlaceholder={translations.targetUploadTitle}
138
+ layout="horizontal"
139
+ variant="portrait"
140
+ />
141
+ </View>
142
+
143
+ <ErrorDisplay error={feature.error} />
144
+
145
+ <View style={styles.buttonContainer}>
146
+ <AtomicButton
147
+ title={
148
+ feature.isProcessing
149
+ ? translations.processingText
150
+ : translations.processButtonText
151
+ }
152
+ onPress={handleProcess}
153
+ disabled={!canProcess}
154
+ variant="primary"
155
+ size="lg"
156
+ />
157
+ </View>
158
+ </ScrollView>
159
+
160
+ {renderProcessingModal?.({ visible: feature.isProcessing, progress: feature.progress })}
161
+ </>
162
+ );
163
+ };
164
+
165
+ const styles = StyleSheet.create({
166
+ container: {
167
+ flex: 1,
168
+ },
169
+ content: {
170
+ paddingVertical: 16,
171
+ },
172
+ description: {
173
+ textAlign: "center",
174
+ marginHorizontal: 24,
175
+ marginBottom: 24,
176
+ lineHeight: 24,
177
+ },
178
+ pickerContainer: {
179
+ marginHorizontal: 16,
180
+ marginBottom: 16,
181
+ },
182
+ successText: {
183
+ textAlign: "center",
184
+ marginBottom: 24,
185
+ },
186
+ resultImageContainer: {
187
+ alignItems: "center",
188
+ marginHorizontal: 24,
189
+ marginBottom: 24,
190
+ },
191
+ resultImage: {
192
+ borderRadius: 16,
193
+ },
194
+ resultActions: {
195
+ marginHorizontal: 24,
196
+ gap: 12,
197
+ },
198
+ buttonContainer: {
199
+ marginHorizontal: 24,
200
+ marginTop: 8,
201
+ },
202
+ });
@@ -0,0 +1,2 @@
1
+ export { FaceSwapFeature } from "./FaceSwapFeature";
2
+ export type { FaceSwapFeatureProps } from "./FaceSwapFeature";
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export * from "./hooks";
6
+ export * from "./components";
@@ -1,34 +1,36 @@
1
1
  /**
2
2
  * PhotoRestoreFeature Component
3
- * Main photo restore feature UI component
3
+ * Self-contained photo restore feature UI component
4
+ * Uses hook internally, only requires config and translations
4
5
  */
5
6
 
6
7
  import React, { useCallback, useMemo } from "react";
7
8
  import { View, ScrollView, StyleSheet } from "react-native";
8
9
  import {
9
10
  useAppDesignTokens,
10
- PhotoUploadCard,
11
11
  AtomicText,
12
12
  AtomicButton,
13
13
  } from "@umituz/react-native-design-system";
14
+ import { PhotoUploadCard } from "../../../../presentation/components/PhotoUploadCard";
14
15
  import { PhotoRestoreResultView } from "./PhotoRestoreResultView";
16
+ import { usePhotoRestoreFeature } from "../hooks";
15
17
  import type {
16
18
  PhotoRestoreTranslations,
17
19
  PhotoRestoreFeatureConfig,
18
20
  } from "../../domain/types";
19
21
 
20
22
  export interface PhotoRestoreFeatureProps {
21
- imageUri: string | null;
22
- processedUrl: string | null;
23
- isProcessing: boolean;
24
- progress: number;
25
- error: string | null;
23
+ /** Feature configuration with provider-specific settings */
24
+ config: PhotoRestoreFeatureConfig;
25
+ /** User ID for the generation request */
26
+ userId: string;
27
+ /** Translations for all UI text */
26
28
  translations: PhotoRestoreTranslations;
27
- config?: PhotoRestoreFeatureConfig;
28
- onSelectImage: () => void;
29
- onProcess: () => void;
30
- onSave: () => void;
31
- onReset: () => void;
29
+ /** Image picker callback */
30
+ onSelectImage: () => Promise<string | null>;
31
+ /** Save image callback */
32
+ onSaveImage: (imageUrl: string) => Promise<void>;
33
+ /** Optional custom processing modal renderer */
32
34
  renderProcessingModal?: (props: {
33
35
  visible: boolean;
34
36
  progress: number;
@@ -36,20 +38,22 @@ export interface PhotoRestoreFeatureProps {
36
38
  }
37
39
 
38
40
  export const PhotoRestoreFeature: React.FC<PhotoRestoreFeatureProps> = ({
39
- imageUri,
40
- processedUrl,
41
- isProcessing,
42
- progress,
43
- error,
41
+ config,
42
+ userId,
44
43
  translations,
45
44
  onSelectImage,
46
- onProcess,
47
- onSave,
48
- onReset,
45
+ onSaveImage,
49
46
  renderProcessingModal,
50
47
  }) => {
51
48
  const tokens = useAppDesignTokens();
52
49
 
50
+ const feature = usePhotoRestoreFeature({
51
+ config,
52
+ userId,
53
+ onSelectImage,
54
+ onSaveImage,
55
+ });
56
+
53
57
  const photoTranslations = useMemo(
54
58
  () => ({
55
59
  tapToUpload: translations.uploadTitle,
@@ -61,10 +65,18 @@ export const PhotoRestoreFeature: React.FC<PhotoRestoreFeatureProps> = ({
61
65
  );
62
66
 
63
67
  const handleProcess = useCallback(() => {
64
- onProcess();
65
- }, [onProcess]);
68
+ void feature.process();
69
+ }, [feature]);
70
+
71
+ const handleSave = useCallback(() => {
72
+ void feature.save();
73
+ }, [feature]);
74
+
75
+ const handleSelectImage = useCallback(() => {
76
+ void feature.selectImage();
77
+ }, [feature]);
66
78
 
67
- if (processedUrl && imageUri) {
79
+ if (feature.processedUrl && feature.imageUri) {
68
80
  return (
69
81
  <ScrollView
70
82
  style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
@@ -72,8 +84,8 @@ export const PhotoRestoreFeature: React.FC<PhotoRestoreFeatureProps> = ({
72
84
  showsVerticalScrollIndicator={false}
73
85
  >
74
86
  <PhotoRestoreResultView
75
- originalUri={imageUri}
76
- processedUri={processedUrl}
87
+ originalUri={feature.imageUri}
88
+ processedUri={feature.processedUrl}
77
89
  translations={{
78
90
  successText: translations.successText,
79
91
  saveButtonText: translations.saveButtonText,
@@ -81,8 +93,8 @@ export const PhotoRestoreFeature: React.FC<PhotoRestoreFeatureProps> = ({
81
93
  beforeLabel: translations.beforeLabel,
82
94
  afterLabel: translations.afterLabel,
83
95
  }}
84
- onSave={onSave}
85
- onReset={onReset}
96
+ onSave={handleSave}
97
+ onReset={feature.reset}
86
98
  />
87
99
  </ScrollView>
88
100
  );
@@ -103,10 +115,10 @@ export const PhotoRestoreFeature: React.FC<PhotoRestoreFeatureProps> = ({
103
115
  </AtomicText>
104
116
 
105
117
  <PhotoUploadCard
106
- imageUri={imageUri}
107
- onPress={onSelectImage}
108
- isValidating={isProcessing}
109
- disabled={isProcessing}
118
+ imageUri={feature.imageUri}
119
+ onPress={handleSelectImage}
120
+ isValidating={feature.isProcessing}
121
+ disabled={feature.isProcessing}
110
122
  translations={photoTranslations}
111
123
  config={{
112
124
  aspectRatio: 1,
@@ -116,7 +128,7 @@ export const PhotoRestoreFeature: React.FC<PhotoRestoreFeatureProps> = ({
116
128
  }}
117
129
  />
118
130
 
119
- {error && (
131
+ {feature.error && (
120
132
  <View
121
133
  style={[
122
134
  styles.errorContainer,
@@ -124,7 +136,7 @@ export const PhotoRestoreFeature: React.FC<PhotoRestoreFeatureProps> = ({
124
136
  ]}
125
137
  >
126
138
  <AtomicText type="bodyMedium" style={{ color: tokens.colors.error }}>
127
- {error}
139
+ {feature.error}
128
140
  </AtomicText>
129
141
  </View>
130
142
  )}
@@ -132,19 +144,19 @@ export const PhotoRestoreFeature: React.FC<PhotoRestoreFeatureProps> = ({
132
144
  <View style={styles.buttonContainer}>
133
145
  <AtomicButton
134
146
  title={
135
- isProcessing
147
+ feature.isProcessing
136
148
  ? translations.processingText
137
149
  : translations.processButtonText
138
150
  }
139
151
  onPress={handleProcess}
140
- disabled={!imageUri || isProcessing}
152
+ disabled={!feature.imageUri || feature.isProcessing}
141
153
  variant="primary"
142
154
  size="lg"
143
155
  />
144
156
  </View>
145
157
  </ScrollView>
146
158
 
147
- {renderProcessingModal?.({ visible: isProcessing, progress })}
159
+ {renderProcessingModal?.({ visible: feature.isProcessing, progress: feature.progress })}
148
160
  </>
149
161
  );
150
162
  };
@@ -25,3 +25,7 @@ export type {
25
25
  UseRemoveBackgroundFeatureProps,
26
26
  UseRemoveBackgroundFeatureReturn,
27
27
  } from "./presentation";
28
+
29
+ // Presentation Components
30
+ export { RemoveBackgroundFeature } from "./presentation";
31
+ export type { RemoveBackgroundFeatureProps } from "./presentation";
@@ -0,0 +1,199 @@
1
+ /**
2
+ * RemoveBackgroundFeature Component
3
+ * Self-contained remove background feature UI component
4
+ * Uses hook internally, only requires config and translations
5
+ */
6
+
7
+ import React, { useCallback, useMemo } from "react";
8
+ import { View, ScrollView, StyleSheet, Image, Dimensions } from "react-native";
9
+ import {
10
+ useAppDesignTokens,
11
+ AtomicText,
12
+ AtomicButton,
13
+ } from "@umituz/react-native-design-system";
14
+ import { PhotoUploadCard } from "../../../../presentation/components/PhotoUploadCard";
15
+ import { ErrorDisplay } from "../../../../presentation/components/display/ErrorDisplay";
16
+ import { useRemoveBackgroundFeature } from "../hooks";
17
+ import type {
18
+ RemoveBackgroundTranslations,
19
+ RemoveBackgroundFeatureConfig,
20
+ } from "../../domain/types";
21
+
22
+ export interface RemoveBackgroundFeatureProps {
23
+ config: RemoveBackgroundFeatureConfig;
24
+ userId: string;
25
+ translations: RemoveBackgroundTranslations;
26
+ onSelectImage: () => Promise<string | null>;
27
+ onSaveImage: (imageUrl: string) => Promise<void>;
28
+ renderProcessingModal?: (props: {
29
+ visible: boolean;
30
+ progress: number;
31
+ }) => React.ReactNode;
32
+ }
33
+
34
+ export const RemoveBackgroundFeature: React.FC<RemoveBackgroundFeatureProps> = ({
35
+ config,
36
+ userId,
37
+ translations,
38
+ onSelectImage,
39
+ onSaveImage,
40
+ renderProcessingModal,
41
+ }) => {
42
+ const tokens = useAppDesignTokens();
43
+
44
+ const feature = useRemoveBackgroundFeature({
45
+ config,
46
+ userId,
47
+ onSelectImage,
48
+ onSaveImage,
49
+ });
50
+
51
+ const photoTranslations = useMemo(
52
+ () => ({
53
+ tapToUpload: translations.uploadTitle,
54
+ selectPhoto: translations.uploadSubtitle,
55
+ change: translations.uploadChange,
56
+ analyzing: translations.uploadAnalyzing,
57
+ }),
58
+ [translations],
59
+ );
60
+
61
+ const handleProcess = useCallback(() => {
62
+ void feature.process();
63
+ }, [feature]);
64
+
65
+ const handleSave = useCallback(() => {
66
+ void feature.save();
67
+ }, [feature]);
68
+
69
+ const handleSelectImage = useCallback(() => {
70
+ void feature.selectImage();
71
+ }, [feature]);
72
+
73
+ if (feature.processedUrl) {
74
+ const screenWidth = Dimensions.get("window").width;
75
+ const imageSize = screenWidth - 48;
76
+
77
+ return (
78
+ <ScrollView
79
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
80
+ contentContainerStyle={styles.content}
81
+ showsVerticalScrollIndicator={false}
82
+ >
83
+ <AtomicText
84
+ type="headlineMedium"
85
+ style={[styles.successText, { color: tokens.colors.success }]}
86
+ >
87
+ {translations.successText}
88
+ </AtomicText>
89
+
90
+ <View style={styles.resultImageContainer}>
91
+ <Image
92
+ source={{ uri: feature.processedUrl }}
93
+ style={[styles.resultImage, { width: imageSize, height: imageSize }]}
94
+ resizeMode="contain"
95
+ />
96
+ </View>
97
+
98
+ <View style={styles.resultActions}>
99
+ <AtomicButton
100
+ title={translations.saveButtonText}
101
+ onPress={handleSave}
102
+ variant="primary"
103
+ size="lg"
104
+ />
105
+ <AtomicButton
106
+ title={translations.tryAnotherText}
107
+ onPress={feature.reset}
108
+ variant="secondary"
109
+ size="lg"
110
+ />
111
+ </View>
112
+ </ScrollView>
113
+ );
114
+ }
115
+
116
+ return (
117
+ <>
118
+ <ScrollView
119
+ style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
120
+ contentContainerStyle={styles.content}
121
+ showsVerticalScrollIndicator={false}
122
+ >
123
+ <AtomicText
124
+ type="bodyLarge"
125
+ style={[styles.description, { color: tokens.colors.textSecondary }]}
126
+ >
127
+ {translations.description}
128
+ </AtomicText>
129
+
130
+ <PhotoUploadCard
131
+ imageUri={feature.imageUri}
132
+ onPress={handleSelectImage}
133
+ isValidating={feature.isProcessing}
134
+ disabled={feature.isProcessing}
135
+ translations={photoTranslations}
136
+ config={{
137
+ aspectRatio: 1,
138
+ borderRadius: 24,
139
+ showValidationStatus: false,
140
+ allowChange: true,
141
+ }}
142
+ />
143
+
144
+ <ErrorDisplay error={feature.error} />
145
+
146
+ <View style={styles.buttonContainer}>
147
+ <AtomicButton
148
+ title={
149
+ feature.isProcessing
150
+ ? translations.processingText
151
+ : translations.processButtonText
152
+ }
153
+ onPress={handleProcess}
154
+ disabled={!feature.imageUri || feature.isProcessing}
155
+ variant="primary"
156
+ size="lg"
157
+ />
158
+ </View>
159
+ </ScrollView>
160
+
161
+ {renderProcessingModal?.({ visible: feature.isProcessing, progress: feature.progress })}
162
+ </>
163
+ );
164
+ };
165
+
166
+ const styles = StyleSheet.create({
167
+ container: {
168
+ flex: 1,
169
+ },
170
+ content: {
171
+ paddingVertical: 16,
172
+ },
173
+ description: {
174
+ textAlign: "center",
175
+ marginHorizontal: 24,
176
+ marginBottom: 24,
177
+ lineHeight: 24,
178
+ },
179
+ successText: {
180
+ textAlign: "center",
181
+ marginBottom: 24,
182
+ },
183
+ resultImageContainer: {
184
+ alignItems: "center",
185
+ marginHorizontal: 24,
186
+ marginBottom: 24,
187
+ },
188
+ resultImage: {
189
+ borderRadius: 16,
190
+ },
191
+ resultActions: {
192
+ marginHorizontal: 24,
193
+ gap: 12,
194
+ },
195
+ buttonContainer: {
196
+ marginHorizontal: 24,
197
+ marginTop: 8,
198
+ },
199
+ });
@@ -0,0 +1,2 @@
1
+ export { RemoveBackgroundFeature } from "./RemoveBackgroundFeature";
2
+ export type { RemoveBackgroundFeatureProps } from "./RemoveBackgroundFeature";
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export * from "./hooks";
6
+ export * from "./components";