@umituz/react-native-ai-generation-content 1.17.24 → 1.17.26
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 +1 -1
- package/src/domain/interfaces/ai-provider.interface.ts +66 -0
- package/src/features/ai-hug/domain/types/ai-hug.types.ts +4 -13
- package/src/features/ai-hug/domain/types/index.ts +0 -1
- package/src/features/ai-hug/index.ts +1 -6
- package/src/features/ai-hug/presentation/components/AIHugFeature.tsx +14 -22
- package/src/features/ai-hug/presentation/hooks/useAIHugFeature.ts +16 -35
- package/src/features/ai-kiss/domain/types/ai-kiss.types.ts +4 -13
- package/src/features/ai-kiss/domain/types/index.ts +0 -1
- package/src/features/ai-kiss/index.ts +1 -6
- package/src/features/ai-kiss/presentation/components/AIKissFeature.tsx +13 -20
- package/src/features/ai-kiss/presentation/hooks/useAIKissFeature.ts +16 -35
- package/src/features/anime-selfie/domain/types/anime-selfie.types.ts +0 -8
- package/src/features/anime-selfie/domain/types/index.ts +0 -1
- package/src/features/anime-selfie/index.ts +0 -5
- package/src/features/anime-selfie/presentation/components/AnimeSelfieFeature.tsx +0 -3
- package/src/features/anime-selfie/presentation/hooks/useAnimeSelfieFeature.ts +9 -27
- package/src/features/face-swap/domain/types/face-swap.types.ts +0 -9
- package/src/features/face-swap/domain/types/index.ts +0 -1
- package/src/features/face-swap/index.ts +0 -5
- package/src/features/face-swap/presentation/components/FaceSwapFeature.tsx +0 -3
- package/src/features/face-swap/presentation/hooks/useFaceSwapFeature.ts +9 -28
- package/src/features/hd-touch-up/domain/types/hd-touch-up.types.ts +0 -8
- package/src/features/hd-touch-up/domain/types/index.ts +0 -1
- package/src/features/hd-touch-up/index.ts +0 -5
- package/src/features/hd-touch-up/presentation/components/HDTouchUpFeature.tsx +0 -3
- package/src/features/hd-touch-up/presentation/hooks/useHDTouchUpFeature.ts +9 -26
- package/src/features/photo-restoration/domain/types/index.ts +0 -1
- package/src/features/photo-restoration/domain/types/photo-restore.types.ts +0 -8
- package/src/features/photo-restoration/index.ts +0 -5
- package/src/features/photo-restoration/presentation/components/PhotoRestoreFeature.tsx +0 -4
- package/src/features/photo-restoration/presentation/hooks/usePhotoRestoreFeature.ts +9 -26
- package/src/features/remove-background/domain/types/index.ts +0 -1
- package/src/features/remove-background/domain/types/remove-background.types.ts +0 -8
- package/src/features/remove-background/index.ts +0 -5
- package/src/features/remove-background/presentation/components/RemoveBackgroundFeature.tsx +0 -3
- package/src/features/remove-background/presentation/hooks/useRemoveBackgroundFeature.ts +9 -26
- package/src/features/remove-object/domain/types/index.ts +0 -1
- package/src/features/remove-object/domain/types/remove-object.types.ts +0 -8
- package/src/features/remove-object/index.ts +0 -5
- package/src/features/remove-object/presentation/components/RemoveObjectFeature.tsx +0 -3
- package/src/features/remove-object/presentation/hooks/useRemoveObjectFeature.ts +9 -24
- package/src/features/replace-background/domain/types/index.ts +0 -1
- package/src/features/replace-background/domain/types/replace-background.types.ts +0 -9
- package/src/features/replace-background/index.ts +55 -82
- package/src/features/replace-background/infrastructure/constants/prompts.constants.ts +6 -6
- package/src/features/replace-background/infrastructure/index.ts +2 -2
- package/src/features/replace-background/presentation/components/ReplaceBackgroundFeature.tsx +0 -3
- package/src/features/replace-background/presentation/hooks/useReplaceBackgroundFeature.ts +8 -23
- package/src/features/upscaling/domain/types/upscale.types.ts +0 -8
- package/src/features/upscaling/index.ts +0 -5
- package/src/features/upscaling/presentation/components/UpscaleFeature.tsx +0 -4
- package/src/features/upscaling/presentation/hooks/useUpscaleFeature.ts +8 -23
- package/src/index.ts +4 -0
- package/src/infrastructure/services/image-feature-executor.service.ts +143 -0
- package/src/infrastructure/services/index.ts +20 -0
- package/src/infrastructure/services/video-feature-executor.service.ts +140 -0
- package/src/features/ai-hug/infrastructure/index.ts +0 -5
- package/src/features/ai-hug/infrastructure/services/ai-hug-executor.ts +0 -96
- package/src/features/ai-hug/infrastructure/services/index.ts +0 -6
- package/src/features/ai-kiss/infrastructure/index.ts +0 -5
- package/src/features/ai-kiss/infrastructure/services/ai-kiss-executor.ts +0 -96
- package/src/features/ai-kiss/infrastructure/services/index.ts +0 -6
- package/src/features/anime-selfie/infrastructure/index.ts +0 -5
- package/src/features/anime-selfie/infrastructure/services/anime-selfie-executor.ts +0 -95
- package/src/features/anime-selfie/infrastructure/services/index.ts +0 -6
- package/src/features/face-swap/infrastructure/index.ts +0 -5
- package/src/features/face-swap/infrastructure/services/face-swap-executor.ts +0 -96
- package/src/features/face-swap/infrastructure/services/index.ts +0 -6
- package/src/features/hd-touch-up/infrastructure/index.ts +0 -1
- package/src/features/hd-touch-up/infrastructure/services/hd-touch-up-executor.ts +0 -97
- package/src/features/hd-touch-up/infrastructure/services/index.ts +0 -2
- package/src/features/photo-restoration/infrastructure/index.ts +0 -1
- package/src/features/photo-restoration/infrastructure/services/index.ts +0 -2
- package/src/features/photo-restoration/infrastructure/services/photo-restore-executor.ts +0 -98
- package/src/features/remove-background/infrastructure/index.ts +0 -5
- package/src/features/remove-background/infrastructure/services/index.ts +0 -6
- package/src/features/remove-background/infrastructure/services/remove-background-executor.ts +0 -95
- package/src/features/remove-object/infrastructure/index.ts +0 -5
- package/src/features/remove-object/infrastructure/services/index.ts +0 -6
- package/src/features/remove-object/infrastructure/services/remove-object-executor.ts +0 -99
- package/src/features/replace-background/infrastructure/services/index.ts +0 -6
- package/src/features/replace-background/infrastructure/services/replace-background-executor.ts +0 -95
- package/src/features/upscaling/infrastructure/index.ts +0 -1
- package/src/features/upscaling/infrastructure/services/index.ts +0 -2
- package/src/features/upscaling/infrastructure/services/upscale-executor.ts +0 -98
package/package.json
CHANGED
|
@@ -44,6 +44,46 @@ export interface SubscribeOptions<T = unknown> {
|
|
|
44
44
|
onResult?: (result: T) => void;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Feature types for image processing (output: image)
|
|
49
|
+
*/
|
|
50
|
+
export type ImageFeatureType =
|
|
51
|
+
| "upscale"
|
|
52
|
+
| "photo-restore"
|
|
53
|
+
| "face-swap"
|
|
54
|
+
| "anime-selfie"
|
|
55
|
+
| "remove-background"
|
|
56
|
+
| "remove-object"
|
|
57
|
+
| "hd-touch-up"
|
|
58
|
+
| "replace-background";
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Feature types for video generation (output: video)
|
|
62
|
+
*/
|
|
63
|
+
export type VideoFeatureType =
|
|
64
|
+
| "ai-hug"
|
|
65
|
+
| "ai-kiss";
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Input data for image features
|
|
69
|
+
*/
|
|
70
|
+
export interface ImageFeatureInputData {
|
|
71
|
+
imageBase64: string;
|
|
72
|
+
targetImageBase64?: string;
|
|
73
|
+
prompt?: string;
|
|
74
|
+
options?: Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Input data for video features
|
|
79
|
+
*/
|
|
80
|
+
export interface VideoFeatureInputData {
|
|
81
|
+
sourceImageBase64: string;
|
|
82
|
+
targetImageBase64: string;
|
|
83
|
+
prompt?: string;
|
|
84
|
+
options?: Record<string, unknown>;
|
|
85
|
+
}
|
|
86
|
+
|
|
47
87
|
/**
|
|
48
88
|
* AI Provider Interface
|
|
49
89
|
* All AI providers must implement this interface
|
|
@@ -73,4 +113,30 @@ export interface IAIProvider {
|
|
|
73
113
|
run<T = unknown>(model: string, input: Record<string, unknown>): Promise<T>;
|
|
74
114
|
|
|
75
115
|
reset(): void;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get model ID for an image feature
|
|
119
|
+
*/
|
|
120
|
+
getImageFeatureModel(feature: ImageFeatureType): string;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Build provider-specific input for an image feature
|
|
124
|
+
*/
|
|
125
|
+
buildImageFeatureInput(
|
|
126
|
+
feature: ImageFeatureType,
|
|
127
|
+
data: ImageFeatureInputData,
|
|
128
|
+
): Record<string, unknown>;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get model ID for a video feature
|
|
132
|
+
*/
|
|
133
|
+
getVideoFeatureModel(feature: VideoFeatureType): string;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Build provider-specific input for a video feature
|
|
137
|
+
*/
|
|
138
|
+
buildVideoFeatureInput(
|
|
139
|
+
feature: VideoFeatureType,
|
|
140
|
+
data: VideoFeatureInputData,
|
|
141
|
+
): Record<string, unknown>;
|
|
76
142
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Hug Feature Types
|
|
3
|
-
* Request, Result, Config types for AI hug generation
|
|
3
|
+
* Request, Result, Config types for AI hug video generation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export interface AIHugOptions {
|
|
@@ -14,13 +14,13 @@ export interface AIHugRequest {
|
|
|
14
14
|
sourceImageBase64?: string;
|
|
15
15
|
targetImageBase64?: string;
|
|
16
16
|
userId: string;
|
|
17
|
+
prompt?: string;
|
|
17
18
|
options?: AIHugOptions;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export interface AIHugResult {
|
|
21
22
|
success: boolean;
|
|
22
|
-
|
|
23
|
-
imageBase64?: string;
|
|
23
|
+
videoUrl?: string;
|
|
24
24
|
error?: string;
|
|
25
25
|
requestId?: string;
|
|
26
26
|
}
|
|
@@ -28,7 +28,7 @@ export interface AIHugResult {
|
|
|
28
28
|
export interface AIHugFeatureState {
|
|
29
29
|
sourceImageUri: string | null;
|
|
30
30
|
targetImageUri: string | null;
|
|
31
|
-
|
|
31
|
+
processedVideoUrl: string | null;
|
|
32
32
|
isProcessing: boolean;
|
|
33
33
|
progress: number;
|
|
34
34
|
error: string | null;
|
|
@@ -49,19 +49,10 @@ export interface AIHugTranslations {
|
|
|
49
49
|
tryAnotherText: string;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export type AIHugInputBuilder = (
|
|
53
|
-
sourceBase64: string,
|
|
54
|
-
targetBase64: string,
|
|
55
|
-
options?: AIHugOptions,
|
|
56
|
-
) => Record<string, unknown>;
|
|
57
|
-
|
|
58
52
|
export type AIHugResultExtractor = (result: unknown) => string | undefined;
|
|
59
53
|
|
|
60
54
|
export interface AIHugFeatureConfig {
|
|
61
|
-
providerId?: string;
|
|
62
55
|
creditCost?: number;
|
|
63
|
-
model: string;
|
|
64
|
-
buildInput: AIHugInputBuilder;
|
|
65
56
|
extractResult?: AIHugResultExtractor;
|
|
66
57
|
prepareImage: (imageUri: string) => Promise<string>;
|
|
67
58
|
onSourceImageSelect?: (uri: string) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Hug Feature
|
|
3
|
-
* Provider-agnostic AI hug generation feature
|
|
3
|
+
* Provider-agnostic AI hug video generation feature
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// Domain Types
|
|
@@ -11,14 +11,9 @@ export type {
|
|
|
11
11
|
AIHugFeatureState,
|
|
12
12
|
AIHugTranslations,
|
|
13
13
|
AIHugFeatureConfig,
|
|
14
|
-
AIHugInputBuilder,
|
|
15
14
|
AIHugResultExtractor,
|
|
16
15
|
} from "./domain";
|
|
17
16
|
|
|
18
|
-
// Infrastructure Services
|
|
19
|
-
export { executeAIHug, hasAIHugSupport } from "./infrastructure";
|
|
20
|
-
export type { ExecuteAIHugOptions } from "./infrastructure";
|
|
21
|
-
|
|
22
17
|
// Presentation Hooks
|
|
23
18
|
export { useAIHugFeature } from "./presentation";
|
|
24
19
|
export type {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AIHugFeature Component
|
|
3
|
-
* Self-contained AI hug feature UI component
|
|
3
|
+
* Self-contained AI hug video feature UI component
|
|
4
4
|
* Uses hook internally, only requires config and translations
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React, { useCallback } from "react";
|
|
8
|
-
import { View, ScrollView, StyleSheet,
|
|
8
|
+
import { View, ScrollView, StyleSheet, Dimensions } from "react-native";
|
|
9
9
|
import {
|
|
10
10
|
useAppDesignTokens,
|
|
11
11
|
AtomicText,
|
|
@@ -22,16 +22,16 @@ import type {
|
|
|
22
22
|
export interface AIHugFeatureProps {
|
|
23
23
|
/** Feature configuration with provider-specific settings */
|
|
24
24
|
config: AIHugFeatureConfig;
|
|
25
|
-
/** User ID for the generation request */
|
|
26
|
-
userId: string;
|
|
27
25
|
/** Translations for all UI text */
|
|
28
26
|
translations: AIHugTranslations;
|
|
29
27
|
/** Source image picker callback */
|
|
30
28
|
onSelectSourceImage: () => Promise<string | null>;
|
|
31
29
|
/** Target image picker callback */
|
|
32
30
|
onSelectTargetImage: () => Promise<string | null>;
|
|
33
|
-
/** Save
|
|
34
|
-
|
|
31
|
+
/** Save video callback */
|
|
32
|
+
onSaveVideo: (videoUrl: string) => Promise<void>;
|
|
33
|
+
/** Custom video player renderer - required for video playback */
|
|
34
|
+
renderVideoPlayer: (props: { videoUrl: string; size: number }) => React.ReactNode;
|
|
35
35
|
/** Optional custom processing modal renderer */
|
|
36
36
|
renderProcessingModal?: (props: {
|
|
37
37
|
visible: boolean;
|
|
@@ -41,21 +41,20 @@ export interface AIHugFeatureProps {
|
|
|
41
41
|
|
|
42
42
|
export const AIHugFeature: React.FC<AIHugFeatureProps> = ({
|
|
43
43
|
config,
|
|
44
|
-
userId,
|
|
45
44
|
translations,
|
|
46
45
|
onSelectSourceImage,
|
|
47
46
|
onSelectTargetImage,
|
|
48
|
-
|
|
47
|
+
onSaveVideo,
|
|
48
|
+
renderVideoPlayer,
|
|
49
49
|
renderProcessingModal,
|
|
50
50
|
}) => {
|
|
51
51
|
const tokens = useAppDesignTokens();
|
|
52
52
|
|
|
53
53
|
const feature = useAIHugFeature({
|
|
54
54
|
config,
|
|
55
|
-
userId,
|
|
56
55
|
onSelectSourceImage,
|
|
57
56
|
onSelectTargetImage,
|
|
58
|
-
|
|
57
|
+
onSaveVideo,
|
|
59
58
|
});
|
|
60
59
|
|
|
61
60
|
const handleProcess = useCallback(() => {
|
|
@@ -76,9 +75,9 @@ export const AIHugFeature: React.FC<AIHugFeatureProps> = ({
|
|
|
76
75
|
|
|
77
76
|
const canProcess = feature.sourceImageUri && feature.targetImageUri && !feature.isProcessing;
|
|
78
77
|
|
|
79
|
-
if (feature.
|
|
78
|
+
if (feature.processedVideoUrl) {
|
|
80
79
|
const screenWidth = Dimensions.get("window").width;
|
|
81
|
-
const
|
|
80
|
+
const videoSize = screenWidth - 48;
|
|
82
81
|
|
|
83
82
|
return (
|
|
84
83
|
<ScrollView
|
|
@@ -93,12 +92,8 @@ export const AIHugFeature: React.FC<AIHugFeatureProps> = ({
|
|
|
93
92
|
{translations.successText}
|
|
94
93
|
</AtomicText>
|
|
95
94
|
|
|
96
|
-
<View style={styles.
|
|
97
|
-
|
|
98
|
-
source={{ uri: feature.processedUrl }}
|
|
99
|
-
style={[styles.resultImage, { width: imageSize, height: imageSize }]}
|
|
100
|
-
resizeMode="contain"
|
|
101
|
-
/>
|
|
95
|
+
<View style={styles.resultVideoContainer}>
|
|
96
|
+
{renderVideoPlayer({ videoUrl: feature.processedVideoUrl, size: videoSize })}
|
|
102
97
|
</View>
|
|
103
98
|
|
|
104
99
|
<View style={styles.resultActions}>
|
|
@@ -190,14 +185,11 @@ const styles = StyleSheet.create({
|
|
|
190
185
|
textAlign: "center",
|
|
191
186
|
marginBottom: 24,
|
|
192
187
|
},
|
|
193
|
-
|
|
188
|
+
resultVideoContainer: {
|
|
194
189
|
alignItems: "center",
|
|
195
190
|
marginHorizontal: 24,
|
|
196
191
|
marginBottom: 24,
|
|
197
192
|
},
|
|
198
|
-
resultImage: {
|
|
199
|
-
borderRadius: 16,
|
|
200
|
-
},
|
|
201
193
|
resultActions: {
|
|
202
194
|
marginHorizontal: 24,
|
|
203
195
|
gap: 12,
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useAIHugFeature Hook
|
|
3
|
-
* Manages AI hug
|
|
3
|
+
* Manages AI hug video generation state and actions
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { useState, useCallback } from "react";
|
|
7
|
-
import {
|
|
7
|
+
import { executeVideoFeature } from "../../../../infrastructure/services";
|
|
8
8
|
import type {
|
|
9
9
|
AIHugFeatureState,
|
|
10
10
|
AIHugFeatureConfig,
|
|
11
11
|
AIHugResult,
|
|
12
12
|
} from "../../domain/types";
|
|
13
13
|
|
|
14
|
-
declare const __DEV__: boolean;
|
|
15
|
-
|
|
16
14
|
export interface UseAIHugFeatureProps {
|
|
17
15
|
config: AIHugFeatureConfig;
|
|
18
|
-
userId: string;
|
|
19
16
|
onSelectSourceImage: () => Promise<string | null>;
|
|
20
17
|
onSelectTargetImage: () => Promise<string | null>;
|
|
21
|
-
|
|
18
|
+
onSaveVideo: (videoUrl: string) => Promise<void>;
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
export interface UseAIHugFeatureReturn extends AIHugFeatureState {
|
|
@@ -32,7 +29,7 @@ export interface UseAIHugFeatureReturn extends AIHugFeatureState {
|
|
|
32
29
|
const initialState: AIHugFeatureState = {
|
|
33
30
|
sourceImageUri: null,
|
|
34
31
|
targetImageUri: null,
|
|
35
|
-
|
|
32
|
+
processedVideoUrl: null,
|
|
36
33
|
isProcessing: false,
|
|
37
34
|
progress: 0,
|
|
38
35
|
error: null,
|
|
@@ -41,7 +38,7 @@ const initialState: AIHugFeatureState = {
|
|
|
41
38
|
export function useAIHugFeature(
|
|
42
39
|
props: UseAIHugFeatureProps,
|
|
43
40
|
): UseAIHugFeatureReturn {
|
|
44
|
-
const { config,
|
|
41
|
+
const { config, onSelectSourceImage, onSelectTargetImage, onSaveVideo } = props;
|
|
45
42
|
const [state, setState] = useState<AIHugFeatureState>(initialState);
|
|
46
43
|
|
|
47
44
|
const selectSourceImage = useCallback(async () => {
|
|
@@ -86,39 +83,23 @@ export function useAIHugFeature(
|
|
|
86
83
|
|
|
87
84
|
config.onProcessingStart?.();
|
|
88
85
|
|
|
89
|
-
if (__DEV__) {
|
|
90
|
-
// eslint-disable-next-line no-console
|
|
91
|
-
console.log("[useAIHugFeature] Starting AI hug process");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
86
|
const sourceImageBase64 = await config.prepareImage(state.sourceImageUri);
|
|
95
87
|
const targetImageBase64 = await config.prepareImage(state.targetImageUri);
|
|
96
88
|
|
|
97
|
-
const result
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
sourceImageBase64,
|
|
102
|
-
targetImageBase64,
|
|
103
|
-
userId,
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
model: config.model,
|
|
107
|
-
buildInput: config.buildInput,
|
|
108
|
-
extractResult: config.extractResult,
|
|
109
|
-
onProgress: handleProgress,
|
|
110
|
-
},
|
|
89
|
+
const result = await executeVideoFeature(
|
|
90
|
+
"ai-hug",
|
|
91
|
+
{ sourceImageBase64, targetImageBase64 },
|
|
92
|
+
{ extractResult: config.extractResult, onProgress: handleProgress },
|
|
111
93
|
);
|
|
112
94
|
|
|
113
|
-
if (result.success && result.
|
|
114
|
-
const url = result.imageUrl;
|
|
95
|
+
if (result.success && result.videoUrl) {
|
|
115
96
|
setState((prev) => ({
|
|
116
97
|
...prev,
|
|
117
98
|
isProcessing: false,
|
|
118
|
-
|
|
99
|
+
processedVideoUrl: result.videoUrl!,
|
|
119
100
|
progress: 100,
|
|
120
101
|
}));
|
|
121
|
-
config.onProcessingComplete?.(result);
|
|
102
|
+
config.onProcessingComplete?.({ success: true, videoUrl: result.videoUrl });
|
|
122
103
|
} else {
|
|
123
104
|
const errorMessage = result.error || "Processing failed";
|
|
124
105
|
setState((prev) => ({
|
|
@@ -129,18 +110,18 @@ export function useAIHugFeature(
|
|
|
129
110
|
}));
|
|
130
111
|
config.onError?.(errorMessage);
|
|
131
112
|
}
|
|
132
|
-
}, [state.sourceImageUri, state.targetImageUri,
|
|
113
|
+
}, [state.sourceImageUri, state.targetImageUri, config, handleProgress]);
|
|
133
114
|
|
|
134
115
|
const save = useCallback(async () => {
|
|
135
|
-
if (!state.
|
|
116
|
+
if (!state.processedVideoUrl) return;
|
|
136
117
|
|
|
137
118
|
try {
|
|
138
|
-
await
|
|
119
|
+
await onSaveVideo(state.processedVideoUrl);
|
|
139
120
|
} catch (error) {
|
|
140
121
|
const message = error instanceof Error ? error.message : String(error);
|
|
141
122
|
setState((prev) => ({ ...prev, error: message }));
|
|
142
123
|
}
|
|
143
|
-
}, [state.
|
|
124
|
+
}, [state.processedVideoUrl, onSaveVideo]);
|
|
144
125
|
|
|
145
126
|
const reset = useCallback(() => {
|
|
146
127
|
setState(initialState);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Kiss Feature Types
|
|
3
|
-
* Request, Result, Config types for AI kiss generation
|
|
3
|
+
* Request, Result, Config types for AI kiss video generation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export interface AIKissOptions {
|
|
@@ -14,13 +14,13 @@ export interface AIKissRequest {
|
|
|
14
14
|
sourceImageBase64?: string;
|
|
15
15
|
targetImageBase64?: string;
|
|
16
16
|
userId: string;
|
|
17
|
+
prompt?: string;
|
|
17
18
|
options?: AIKissOptions;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export interface AIKissResult {
|
|
21
22
|
success: boolean;
|
|
22
|
-
|
|
23
|
-
imageBase64?: string;
|
|
23
|
+
videoUrl?: string;
|
|
24
24
|
error?: string;
|
|
25
25
|
requestId?: string;
|
|
26
26
|
}
|
|
@@ -28,7 +28,7 @@ export interface AIKissResult {
|
|
|
28
28
|
export interface AIKissFeatureState {
|
|
29
29
|
sourceImageUri: string | null;
|
|
30
30
|
targetImageUri: string | null;
|
|
31
|
-
|
|
31
|
+
processedVideoUrl: string | null;
|
|
32
32
|
isProcessing: boolean;
|
|
33
33
|
progress: number;
|
|
34
34
|
error: string | null;
|
|
@@ -49,19 +49,10 @@ export interface AIKissTranslations {
|
|
|
49
49
|
tryAnotherText: string;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export type AIKissInputBuilder = (
|
|
53
|
-
sourceBase64: string,
|
|
54
|
-
targetBase64: string,
|
|
55
|
-
options?: AIKissOptions,
|
|
56
|
-
) => Record<string, unknown>;
|
|
57
|
-
|
|
58
52
|
export type AIKissResultExtractor = (result: unknown) => string | undefined;
|
|
59
53
|
|
|
60
54
|
export interface AIKissFeatureConfig {
|
|
61
|
-
providerId?: string;
|
|
62
55
|
creditCost?: number;
|
|
63
|
-
model: string;
|
|
64
|
-
buildInput: AIKissInputBuilder;
|
|
65
56
|
extractResult?: AIKissResultExtractor;
|
|
66
57
|
prepareImage: (imageUri: string) => Promise<string>;
|
|
67
58
|
onSourceImageSelect?: (uri: string) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI Kiss Feature
|
|
3
|
-
* Provider-agnostic AI kiss generation feature
|
|
3
|
+
* Provider-agnostic AI kiss video generation feature
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// Domain Types
|
|
@@ -11,14 +11,9 @@ export type {
|
|
|
11
11
|
AIKissFeatureState,
|
|
12
12
|
AIKissTranslations,
|
|
13
13
|
AIKissFeatureConfig,
|
|
14
|
-
AIKissInputBuilder,
|
|
15
14
|
AIKissResultExtractor,
|
|
16
15
|
} from "./domain";
|
|
17
16
|
|
|
18
|
-
// Infrastructure Services
|
|
19
|
-
export { executeAIKiss, hasAIKissSupport } from "./infrastructure";
|
|
20
|
-
export type { ExecuteAIKissOptions } from "./infrastructure";
|
|
21
|
-
|
|
22
17
|
// Presentation Hooks
|
|
23
18
|
export { useAIKissFeature } from "./presentation";
|
|
24
19
|
export type {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AIKissFeature Component
|
|
3
|
-
* Self-contained AI kiss feature UI component
|
|
3
|
+
* Self-contained AI kiss video feature UI component
|
|
4
4
|
* Uses hook internally, only requires config and translations
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React, { useCallback } from "react";
|
|
8
|
-
import { View, ScrollView, StyleSheet,
|
|
8
|
+
import { View, ScrollView, StyleSheet, Dimensions } from "react-native";
|
|
9
9
|
import {
|
|
10
10
|
useAppDesignTokens,
|
|
11
11
|
AtomicText,
|
|
@@ -21,11 +21,12 @@ import type {
|
|
|
21
21
|
|
|
22
22
|
export interface AIKissFeatureProps {
|
|
23
23
|
config: AIKissFeatureConfig;
|
|
24
|
-
userId: string;
|
|
25
24
|
translations: AIKissTranslations;
|
|
26
25
|
onSelectSourceImage: () => Promise<string | null>;
|
|
27
26
|
onSelectTargetImage: () => Promise<string | null>;
|
|
28
|
-
|
|
27
|
+
onSaveVideo: (videoUrl: string) => Promise<void>;
|
|
28
|
+
/** Custom video player renderer - required for video playback */
|
|
29
|
+
renderVideoPlayer: (props: { videoUrl: string; size: number }) => React.ReactNode;
|
|
29
30
|
renderProcessingModal?: (props: {
|
|
30
31
|
visible: boolean;
|
|
31
32
|
progress: number;
|
|
@@ -34,21 +35,20 @@ export interface AIKissFeatureProps {
|
|
|
34
35
|
|
|
35
36
|
export const AIKissFeature: React.FC<AIKissFeatureProps> = ({
|
|
36
37
|
config,
|
|
37
|
-
userId,
|
|
38
38
|
translations,
|
|
39
39
|
onSelectSourceImage,
|
|
40
40
|
onSelectTargetImage,
|
|
41
|
-
|
|
41
|
+
onSaveVideo,
|
|
42
|
+
renderVideoPlayer,
|
|
42
43
|
renderProcessingModal,
|
|
43
44
|
}) => {
|
|
44
45
|
const tokens = useAppDesignTokens();
|
|
45
46
|
|
|
46
47
|
const feature = useAIKissFeature({
|
|
47
48
|
config,
|
|
48
|
-
userId,
|
|
49
49
|
onSelectSourceImage,
|
|
50
50
|
onSelectTargetImage,
|
|
51
|
-
|
|
51
|
+
onSaveVideo,
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
const handleProcess = useCallback(() => {
|
|
@@ -69,9 +69,9 @@ export const AIKissFeature: React.FC<AIKissFeatureProps> = ({
|
|
|
69
69
|
|
|
70
70
|
const canProcess = feature.sourceImageUri && feature.targetImageUri && !feature.isProcessing;
|
|
71
71
|
|
|
72
|
-
if (feature.
|
|
72
|
+
if (feature.processedVideoUrl) {
|
|
73
73
|
const screenWidth = Dimensions.get("window").width;
|
|
74
|
-
const
|
|
74
|
+
const videoSize = screenWidth - 48;
|
|
75
75
|
|
|
76
76
|
return (
|
|
77
77
|
<ScrollView
|
|
@@ -86,12 +86,8 @@ export const AIKissFeature: React.FC<AIKissFeatureProps> = ({
|
|
|
86
86
|
{translations.successText}
|
|
87
87
|
</AtomicText>
|
|
88
88
|
|
|
89
|
-
<View style={styles.
|
|
90
|
-
|
|
91
|
-
source={{ uri: feature.processedUrl }}
|
|
92
|
-
style={[styles.resultImage, { width: imageSize, height: imageSize }]}
|
|
93
|
-
resizeMode="contain"
|
|
94
|
-
/>
|
|
89
|
+
<View style={styles.resultVideoContainer}>
|
|
90
|
+
{renderVideoPlayer({ videoUrl: feature.processedVideoUrl, size: videoSize })}
|
|
95
91
|
</View>
|
|
96
92
|
|
|
97
93
|
<View style={styles.resultActions}>
|
|
@@ -183,14 +179,11 @@ const styles = StyleSheet.create({
|
|
|
183
179
|
textAlign: "center",
|
|
184
180
|
marginBottom: 24,
|
|
185
181
|
},
|
|
186
|
-
|
|
182
|
+
resultVideoContainer: {
|
|
187
183
|
alignItems: "center",
|
|
188
184
|
marginHorizontal: 24,
|
|
189
185
|
marginBottom: 24,
|
|
190
186
|
},
|
|
191
|
-
resultImage: {
|
|
192
|
-
borderRadius: 16,
|
|
193
|
-
},
|
|
194
187
|
resultActions: {
|
|
195
188
|
marginHorizontal: 24,
|
|
196
189
|
gap: 12,
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useAIKissFeature Hook
|
|
3
|
-
* Manages AI kiss
|
|
3
|
+
* Manages AI kiss video generation state and actions
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { useState, useCallback } from "react";
|
|
7
|
-
import {
|
|
7
|
+
import { executeVideoFeature } from "../../../../infrastructure/services";
|
|
8
8
|
import type {
|
|
9
9
|
AIKissFeatureState,
|
|
10
10
|
AIKissFeatureConfig,
|
|
11
11
|
AIKissResult,
|
|
12
12
|
} from "../../domain/types";
|
|
13
13
|
|
|
14
|
-
declare const __DEV__: boolean;
|
|
15
|
-
|
|
16
14
|
export interface UseAIKissFeatureProps {
|
|
17
15
|
config: AIKissFeatureConfig;
|
|
18
|
-
userId: string;
|
|
19
16
|
onSelectSourceImage: () => Promise<string | null>;
|
|
20
17
|
onSelectTargetImage: () => Promise<string | null>;
|
|
21
|
-
|
|
18
|
+
onSaveVideo: (videoUrl: string) => Promise<void>;
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
export interface UseAIKissFeatureReturn extends AIKissFeatureState {
|
|
@@ -32,7 +29,7 @@ export interface UseAIKissFeatureReturn extends AIKissFeatureState {
|
|
|
32
29
|
const initialState: AIKissFeatureState = {
|
|
33
30
|
sourceImageUri: null,
|
|
34
31
|
targetImageUri: null,
|
|
35
|
-
|
|
32
|
+
processedVideoUrl: null,
|
|
36
33
|
isProcessing: false,
|
|
37
34
|
progress: 0,
|
|
38
35
|
error: null,
|
|
@@ -41,7 +38,7 @@ const initialState: AIKissFeatureState = {
|
|
|
41
38
|
export function useAIKissFeature(
|
|
42
39
|
props: UseAIKissFeatureProps,
|
|
43
40
|
): UseAIKissFeatureReturn {
|
|
44
|
-
const { config,
|
|
41
|
+
const { config, onSelectSourceImage, onSelectTargetImage, onSaveVideo } = props;
|
|
45
42
|
const [state, setState] = useState<AIKissFeatureState>(initialState);
|
|
46
43
|
|
|
47
44
|
const selectSourceImage = useCallback(async () => {
|
|
@@ -86,39 +83,23 @@ export function useAIKissFeature(
|
|
|
86
83
|
|
|
87
84
|
config.onProcessingStart?.();
|
|
88
85
|
|
|
89
|
-
if (__DEV__) {
|
|
90
|
-
// eslint-disable-next-line no-console
|
|
91
|
-
console.log("[useAIKissFeature] Starting AI kiss process");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
86
|
const sourceImageBase64 = await config.prepareImage(state.sourceImageUri);
|
|
95
87
|
const targetImageBase64 = await config.prepareImage(state.targetImageUri);
|
|
96
88
|
|
|
97
|
-
const result
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
sourceImageBase64,
|
|
102
|
-
targetImageBase64,
|
|
103
|
-
userId,
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
model: config.model,
|
|
107
|
-
buildInput: config.buildInput,
|
|
108
|
-
extractResult: config.extractResult,
|
|
109
|
-
onProgress: handleProgress,
|
|
110
|
-
},
|
|
89
|
+
const result = await executeVideoFeature(
|
|
90
|
+
"ai-kiss",
|
|
91
|
+
{ sourceImageBase64, targetImageBase64 },
|
|
92
|
+
{ extractResult: config.extractResult, onProgress: handleProgress },
|
|
111
93
|
);
|
|
112
94
|
|
|
113
|
-
if (result.success && result.
|
|
114
|
-
const url = result.imageUrl;
|
|
95
|
+
if (result.success && result.videoUrl) {
|
|
115
96
|
setState((prev) => ({
|
|
116
97
|
...prev,
|
|
117
98
|
isProcessing: false,
|
|
118
|
-
|
|
99
|
+
processedVideoUrl: result.videoUrl!,
|
|
119
100
|
progress: 100,
|
|
120
101
|
}));
|
|
121
|
-
config.onProcessingComplete?.(result);
|
|
102
|
+
config.onProcessingComplete?.({ success: true, videoUrl: result.videoUrl });
|
|
122
103
|
} else {
|
|
123
104
|
const errorMessage = result.error || "Processing failed";
|
|
124
105
|
setState((prev) => ({
|
|
@@ -129,18 +110,18 @@ export function useAIKissFeature(
|
|
|
129
110
|
}));
|
|
130
111
|
config.onError?.(errorMessage);
|
|
131
112
|
}
|
|
132
|
-
}, [state.sourceImageUri, state.targetImageUri,
|
|
113
|
+
}, [state.sourceImageUri, state.targetImageUri, config, handleProgress]);
|
|
133
114
|
|
|
134
115
|
const save = useCallback(async () => {
|
|
135
|
-
if (!state.
|
|
116
|
+
if (!state.processedVideoUrl) return;
|
|
136
117
|
|
|
137
118
|
try {
|
|
138
|
-
await
|
|
119
|
+
await onSaveVideo(state.processedVideoUrl);
|
|
139
120
|
} catch (error) {
|
|
140
121
|
const message = error instanceof Error ? error.message : String(error);
|
|
141
122
|
setState((prev) => ({ ...prev, error: message }));
|
|
142
123
|
}
|
|
143
|
-
}, [state.
|
|
124
|
+
}, [state.processedVideoUrl, onSaveVideo]);
|
|
144
125
|
|
|
145
126
|
const reset = useCallback(() => {
|
|
146
127
|
setState(initialState);
|