@umituz/react-native-ai-generation-content 1.21.1 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/features/scenarios/domain/types.ts +46 -10
- package/src/features/scenarios/presentation/containers/CategoryNavigationContainer.tsx +65 -1
- package/src/features/scenarios/presentation/screens/HierarchicalScenarioListScreen.tsx +38 -9
- package/src/features/scenarios/presentation/screens/MainCategoryScreen.tsx +16 -4
- package/src/features/scenarios/presentation/screens/SubCategoryScreen.tsx +31 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -14,18 +14,54 @@ export enum ScenarioCategory {
|
|
|
14
14
|
CULTURAL = "cultural",
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Output type for AI generation
|
|
19
|
+
*/
|
|
20
|
+
export type ScenarioOutputType = 'image' | 'video' | 'both';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Scenario media configuration
|
|
24
|
+
*/
|
|
25
|
+
export interface ScenarioMedia {
|
|
26
|
+
readonly imageUrl?: string; // Preview/thumbnail image
|
|
27
|
+
readonly videoUrl?: string; // Preview video URL
|
|
28
|
+
readonly previewImageUrl?: string; // Smaller thumbnail
|
|
29
|
+
}
|
|
30
|
+
|
|
17
31
|
export interface ScenarioData {
|
|
18
32
|
readonly id: string;
|
|
19
33
|
readonly category?: ScenarioCategory | string;
|
|
34
|
+
|
|
35
|
+
// Content (app provides in target language)
|
|
20
36
|
readonly title: string;
|
|
21
37
|
readonly description: string;
|
|
38
|
+
|
|
39
|
+
// AI Configuration
|
|
40
|
+
readonly outputType: ScenarioOutputType; // What this scenario generates
|
|
41
|
+
readonly aiPrompt: string; // AI generation prompt
|
|
42
|
+
readonly storyTemplate?: string; // Story template with placeholders (optional)
|
|
43
|
+
|
|
44
|
+
// Media
|
|
22
45
|
readonly icon: string;
|
|
23
|
-
readonly imageUrl?: string;
|
|
24
|
-
readonly
|
|
25
|
-
readonly
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
readonly
|
|
46
|
+
readonly imageUrl?: string; // Preview image
|
|
47
|
+
readonly videoUrl?: string; // Preview video
|
|
48
|
+
readonly previewImageUrl?: string; // Thumbnail
|
|
49
|
+
|
|
50
|
+
// Requirements
|
|
51
|
+
readonly requiresPhoto?: boolean; // Requires user photo upload
|
|
52
|
+
readonly requiresMultiplePhotos?: boolean; // Requires multiple photos (e.g., couples)
|
|
53
|
+
readonly minPhotos?: number; // Minimum photos required
|
|
54
|
+
readonly maxPhotos?: number; // Maximum photos allowed
|
|
55
|
+
|
|
56
|
+
// Display
|
|
57
|
+
readonly hidden?: boolean; // Hide from UI
|
|
58
|
+
readonly featured?: boolean; // Featured/promoted scenario
|
|
59
|
+
readonly order?: number; // Display order
|
|
60
|
+
|
|
61
|
+
// Metadata
|
|
62
|
+
readonly tags?: readonly string[]; // Search/filter tags
|
|
63
|
+
readonly duration?: number; // Video duration (for video scenarios)
|
|
64
|
+
readonly aspectRatio?: string; // Output aspect ratio (e.g., "16:9", "9:16")
|
|
29
65
|
}
|
|
30
66
|
|
|
31
67
|
/**
|
|
@@ -33,8 +69,8 @@ export interface ScenarioData {
|
|
|
33
69
|
*/
|
|
34
70
|
export interface ScenarioMainCategory {
|
|
35
71
|
readonly id: string;
|
|
36
|
-
readonly
|
|
37
|
-
readonly
|
|
72
|
+
readonly title: string;
|
|
73
|
+
readonly description?: string;
|
|
38
74
|
readonly icon?: string;
|
|
39
75
|
readonly emoji?: string;
|
|
40
76
|
readonly order: number;
|
|
@@ -46,8 +82,8 @@ export interface ScenarioMainCategory {
|
|
|
46
82
|
*/
|
|
47
83
|
export interface ScenarioSubCategory {
|
|
48
84
|
readonly id: string;
|
|
49
|
-
readonly
|
|
50
|
-
readonly
|
|
85
|
+
readonly title: string;
|
|
86
|
+
readonly description?: string;
|
|
51
87
|
readonly icon?: string;
|
|
52
88
|
readonly emoji?: string;
|
|
53
89
|
readonly mainCategoryId: string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Main Category → Sub Category → Scenario List
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, { useState, useCallback } from "react";
|
|
7
|
+
import React, { useState, useCallback, useEffect } from "react";
|
|
8
8
|
import type {
|
|
9
9
|
ScenarioData,
|
|
10
10
|
ScenarioMainCategory,
|
|
@@ -45,22 +45,61 @@ export const CategoryNavigationContainer: React.FC<
|
|
|
45
45
|
const [selectedMainCategoryId, setSelectedMainCategoryId] = useState<string | null>(null);
|
|
46
46
|
const [selectedSubCategoryId, setSelectedSubCategoryId] = useState<string | null>(null);
|
|
47
47
|
|
|
48
|
+
// Debug: Initial mount
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
51
|
+
console.log("[CategoryNavigationContainer] Mounted", {
|
|
52
|
+
mainCategoriesCount: mainCategories.length,
|
|
53
|
+
subCategoriesCount: subCategories.length,
|
|
54
|
+
scenariosCount: scenarios.length,
|
|
55
|
+
currentStep,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
// Debug: Step changes
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
63
|
+
console.log("[CategoryNavigationContainer] Step changed", {
|
|
64
|
+
currentStep,
|
|
65
|
+
selectedMainCategoryId,
|
|
66
|
+
selectedSubCategoryId,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}, [currentStep, selectedMainCategoryId, selectedSubCategoryId]);
|
|
70
|
+
|
|
48
71
|
const handleSelectMainCategory = useCallback((categoryId: string) => {
|
|
72
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
73
|
+
console.log("[CategoryNavigationContainer] Main category selected", {
|
|
74
|
+
categoryId,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
49
77
|
setSelectedMainCategoryId(categoryId);
|
|
50
78
|
setCurrentStep("sub_category");
|
|
51
79
|
}, []);
|
|
52
80
|
|
|
53
81
|
const handleSelectSubCategory = useCallback((subCategoryId: string) => {
|
|
82
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
83
|
+
console.log("[CategoryNavigationContainer] Sub category selected", {
|
|
84
|
+
subCategoryId,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
54
87
|
setSelectedSubCategoryId(subCategoryId);
|
|
55
88
|
setCurrentStep("scenario_list");
|
|
56
89
|
}, []);
|
|
57
90
|
|
|
58
91
|
const handleBackFromSubCategory = useCallback(() => {
|
|
92
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
93
|
+
console.log("[CategoryNavigationContainer] Back from sub category");
|
|
94
|
+
}
|
|
59
95
|
setSelectedMainCategoryId(null);
|
|
60
96
|
setCurrentStep("main_category");
|
|
61
97
|
}, []);
|
|
62
98
|
|
|
63
99
|
const handleBackFromScenarioList = useCallback(() => {
|
|
100
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
101
|
+
console.log("[CategoryNavigationContainer] Back from scenario list");
|
|
102
|
+
}
|
|
64
103
|
setSelectedSubCategoryId(null);
|
|
65
104
|
setCurrentStep("sub_category");
|
|
66
105
|
}, []);
|
|
@@ -72,6 +111,11 @@ export const CategoryNavigationContainer: React.FC<
|
|
|
72
111
|
}, [onBack]);
|
|
73
112
|
|
|
74
113
|
if (currentStep === "main_category") {
|
|
114
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
115
|
+
console.log("[CategoryNavigationContainer] Rendering MainCategoryScreen", {
|
|
116
|
+
mainCategoriesCount: mainCategories.length,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
75
119
|
return (
|
|
76
120
|
<MainCategoryScreen
|
|
77
121
|
mainCategories={mainCategories}
|
|
@@ -85,6 +129,12 @@ export const CategoryNavigationContainer: React.FC<
|
|
|
85
129
|
}
|
|
86
130
|
|
|
87
131
|
if (currentStep === "sub_category" && selectedMainCategoryId) {
|
|
132
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
133
|
+
console.log("[CategoryNavigationContainer] Rendering SubCategoryScreen", {
|
|
134
|
+
selectedMainCategoryId,
|
|
135
|
+
subCategoriesCount: subCategories.length,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
88
138
|
return (
|
|
89
139
|
<SubCategoryScreen
|
|
90
140
|
mainCategoryId={selectedMainCategoryId}
|
|
@@ -97,6 +147,12 @@ export const CategoryNavigationContainer: React.FC<
|
|
|
97
147
|
}
|
|
98
148
|
|
|
99
149
|
if (currentStep === "scenario_list" && selectedSubCategoryId) {
|
|
150
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
151
|
+
console.log("[CategoryNavigationContainer] Rendering HierarchicalScenarioListScreen", {
|
|
152
|
+
selectedSubCategoryId,
|
|
153
|
+
scenariosCount: scenarios.length,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
100
156
|
return (
|
|
101
157
|
<HierarchicalScenarioListScreen
|
|
102
158
|
subCategoryId={selectedSubCategoryId}
|
|
@@ -110,5 +166,13 @@ export const CategoryNavigationContainer: React.FC<
|
|
|
110
166
|
);
|
|
111
167
|
}
|
|
112
168
|
|
|
169
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
170
|
+
console.log("[CategoryNavigationContainer] Rendering NULL - no matching condition", {
|
|
171
|
+
currentStep,
|
|
172
|
+
selectedMainCategoryId,
|
|
173
|
+
selectedSubCategoryId,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
113
177
|
return null;
|
|
114
178
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* PERFORMANCE OPTIMIZED: No FlatList key remounting, memoized calculations
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React, { useMemo, useCallback, useState } from "react";
|
|
7
|
+
import React, { useMemo, useCallback, useState, useEffect } from "react";
|
|
8
8
|
import {
|
|
9
9
|
View,
|
|
10
10
|
FlatList,
|
|
@@ -56,13 +56,44 @@ export const HierarchicalScenarioListScreen: React.FC<HierarchicalScenarioListSc
|
|
|
56
56
|
);
|
|
57
57
|
|
|
58
58
|
const filteredScenarios = useMemo(() => {
|
|
59
|
-
if (!subCategory)
|
|
59
|
+
if (!subCategory) {
|
|
60
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
61
|
+
console.log("[HierarchicalScenarioListScreen] No subCategory found", {
|
|
62
|
+
subCategoryId,
|
|
63
|
+
subCategoriesCount: subCategories.length,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
const filtered = scenarios.filter((scenario) => {
|
|
62
70
|
if (!scenario.category) return false;
|
|
63
71
|
return subCategory.scenarioCategories.includes(scenario.category);
|
|
64
72
|
});
|
|
65
|
-
|
|
73
|
+
|
|
74
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
75
|
+
console.log("[HierarchicalScenarioListScreen] Filtered scenarios", {
|
|
76
|
+
subCategoryId: subCategory.id,
|
|
77
|
+
scenarioCategories: subCategory.scenarioCategories,
|
|
78
|
+
totalScenarios: scenarios.length,
|
|
79
|
+
filteredCount: filtered.length,
|
|
80
|
+
sampleScenarioCategories: scenarios.slice(0, 5).map(s => s.category),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return filtered;
|
|
85
|
+
}, [scenarios, subCategory, subCategoryId, subCategories]);
|
|
86
|
+
|
|
87
|
+
// Debug: Monitor component state
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
90
|
+
console.log("[HierarchicalScenarioListScreen] Component state", {
|
|
91
|
+
subCategoryId,
|
|
92
|
+
hasSubCategory: !!subCategory,
|
|
93
|
+
filteredScenariosCount: filteredScenarios.length,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}, [subCategoryId, subCategory, filteredScenarios]);
|
|
66
97
|
|
|
67
98
|
const horizontalPadding = tokens.spacing.md;
|
|
68
99
|
const cardSpacing = tokens.spacing.md;
|
|
@@ -91,15 +122,13 @@ export const HierarchicalScenarioListScreen: React.FC<HierarchicalScenarioListSc
|
|
|
91
122
|
|
|
92
123
|
const renderItem = useCallback(
|
|
93
124
|
({ item }: ListRenderItemInfo<ScenarioData>) => {
|
|
94
|
-
const title = t(`scenario.${item.id}.title`);
|
|
95
|
-
const description = t(`scenario.${item.id}.description`);
|
|
96
125
|
const isSelected = selectedId === item.id;
|
|
97
126
|
|
|
98
127
|
return (
|
|
99
128
|
<AtomicCard
|
|
100
129
|
image={item.previewImageUrl || item.imageUrl || ""}
|
|
101
|
-
title={title}
|
|
102
|
-
subtitle={description}
|
|
130
|
+
title={item.title}
|
|
131
|
+
subtitle={item.description}
|
|
103
132
|
imageAspectRatio={1.25}
|
|
104
133
|
selected={isSelected}
|
|
105
134
|
style={{ width: cardWidth }}
|
|
@@ -108,7 +137,7 @@ export const HierarchicalScenarioListScreen: React.FC<HierarchicalScenarioListSc
|
|
|
108
137
|
/>
|
|
109
138
|
);
|
|
110
139
|
},
|
|
111
|
-
[cardWidth, selectedId,
|
|
140
|
+
[cardWidth, selectedId, handleCardPress]
|
|
112
141
|
);
|
|
113
142
|
|
|
114
143
|
const ListEmptyComponent = useMemo(
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Displays main categories for hierarchical scenario selection
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React, { useMemo, useCallback } from "react";
|
|
6
|
+
import React, { useMemo, useCallback, useEffect } from "react";
|
|
7
7
|
import {
|
|
8
8
|
View,
|
|
9
9
|
FlatList,
|
|
@@ -42,10 +42,22 @@ export const MainCategoryScreen: React.FC<MainCategoryScreenProps> = ({
|
|
|
42
42
|
const tokens = useAppDesignTokens();
|
|
43
43
|
const insets = useSafeAreaInsets();
|
|
44
44
|
|
|
45
|
+
// Debug: Monitor component state
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
48
|
+
console.log("[MainCategoryScreen] Component mounted/updated", {
|
|
49
|
+
mainCategoriesCount: mainCategories.length,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}, [mainCategories]);
|
|
53
|
+
|
|
45
54
|
const styles = useMemo(() => createStyles(tokens), [tokens]);
|
|
46
55
|
|
|
47
56
|
const handleCategoryPress = useCallback(
|
|
48
57
|
(categoryId: string) => {
|
|
58
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
59
|
+
console.log("[MainCategoryScreen] Category pressed", { categoryId });
|
|
60
|
+
}
|
|
49
61
|
onSelectCategory(categoryId);
|
|
50
62
|
},
|
|
51
63
|
[onSelectCategory]
|
|
@@ -53,8 +65,8 @@ export const MainCategoryScreen: React.FC<MainCategoryScreenProps> = ({
|
|
|
53
65
|
|
|
54
66
|
const renderItem = useCallback(
|
|
55
67
|
({ item }: ListRenderItemInfo<ScenarioMainCategory>) => {
|
|
56
|
-
const title =
|
|
57
|
-
const description = item.
|
|
68
|
+
const title = item.title;
|
|
69
|
+
const description = item.description || "";
|
|
58
70
|
|
|
59
71
|
return (
|
|
60
72
|
<TouchableOpacity
|
|
@@ -109,7 +121,7 @@ export const MainCategoryScreen: React.FC<MainCategoryScreenProps> = ({
|
|
|
109
121
|
</TouchableOpacity>
|
|
110
122
|
);
|
|
111
123
|
},
|
|
112
|
-
[
|
|
124
|
+
[tokens, styles, handleCategoryPress]
|
|
113
125
|
);
|
|
114
126
|
|
|
115
127
|
return (
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Displays sub-categories for a selected main category
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React, { useMemo, useCallback } from "react";
|
|
6
|
+
import React, { useMemo, useCallback, useEffect } from "react";
|
|
7
7
|
import {
|
|
8
8
|
View,
|
|
9
9
|
FlatList,
|
|
@@ -44,15 +44,38 @@ export const SubCategoryScreen: React.FC<SubCategoryScreenProps> = ({
|
|
|
44
44
|
const tokens = useAppDesignTokens();
|
|
45
45
|
const insets = useSafeAreaInsets();
|
|
46
46
|
|
|
47
|
-
const filteredSubCategories = useMemo(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
const filteredSubCategories = useMemo(() => {
|
|
48
|
+
const filtered = subCategories.filter((sub) => sub.mainCategoryId === mainCategoryId);
|
|
49
|
+
|
|
50
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
51
|
+
console.log("[SubCategoryScreen] Filtered sub-categories", {
|
|
52
|
+
mainCategoryId,
|
|
53
|
+
totalSubCategories: subCategories.length,
|
|
54
|
+
filteredCount: filtered.length,
|
|
55
|
+
sampleMainCategoryIds: subCategories.slice(0, 5).map(s => s.mainCategoryId),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return filtered;
|
|
60
|
+
}, [subCategories, mainCategoryId]);
|
|
61
|
+
|
|
62
|
+
// Debug: Monitor component state
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
65
|
+
console.log("[SubCategoryScreen] Component mounted/updated", {
|
|
66
|
+
mainCategoryId,
|
|
67
|
+
filteredSubCategoriesCount: filteredSubCategories.length,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}, [mainCategoryId, filteredSubCategories]);
|
|
51
71
|
|
|
52
72
|
const styles = useMemo(() => createStyles(tokens), [tokens]);
|
|
53
73
|
|
|
54
74
|
const handleSubCategoryPress = useCallback(
|
|
55
75
|
(subCategoryId: string) => {
|
|
76
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
77
|
+
console.log("[SubCategoryScreen] Sub-category pressed", { subCategoryId });
|
|
78
|
+
}
|
|
56
79
|
onSelectSubCategory(subCategoryId);
|
|
57
80
|
},
|
|
58
81
|
[onSelectSubCategory]
|
|
@@ -60,8 +83,8 @@ export const SubCategoryScreen: React.FC<SubCategoryScreenProps> = ({
|
|
|
60
83
|
|
|
61
84
|
const renderItem = useCallback(
|
|
62
85
|
({ item }: ListRenderItemInfo<ScenarioSubCategory>) => {
|
|
63
|
-
const title =
|
|
64
|
-
const description = item.
|
|
86
|
+
const title = item.title;
|
|
87
|
+
const description = item.description || "";
|
|
65
88
|
|
|
66
89
|
return (
|
|
67
90
|
<TouchableOpacity
|
|
@@ -116,7 +139,7 @@ export const SubCategoryScreen: React.FC<SubCategoryScreenProps> = ({
|
|
|
116
139
|
</TouchableOpacity>
|
|
117
140
|
);
|
|
118
141
|
},
|
|
119
|
-
[
|
|
142
|
+
[tokens, styles, handleSubCategoryPress]
|
|
120
143
|
);
|
|
121
144
|
|
|
122
145
|
return (
|