@umituz/react-native-ai-generation-content 1.26.56 → 1.26.58
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/domains/generation/wizard/presentation/components/WizardStepRenderer.tsx +18 -54
- package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.types.ts +23 -0
- package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.utils.ts +25 -0
- package/src/domains/result-preview/presentation/components/ResultPreviewScreen.tsx +16 -34
- package/src/domains/result-preview/presentation/components/ResultPreviewScreen.utils.ts +27 -0
- package/src/domains/generation/wizard/infrastructure/utils/media-url-extractor.ts +0 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.58",
|
|
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",
|
|
@@ -1,40 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wizard Step Renderer Component
|
|
3
|
-
* Renders the appropriate screen based on current step type
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import React from "react";
|
|
7
|
-
import { getMediaTypeFromUrl } from "@umituz/react-native-design-system";
|
|
8
|
-
import { StepType
|
|
9
|
-
import type { WizardStepConfig } from "../../domain/entities/wizard-config.types";
|
|
10
|
-
import type { WizardScenarioData } from "../hooks/useWizardGeneration";
|
|
11
|
-
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
2
|
+
import { getMediaTypeFromUrl, extractMediaUrl } from "@umituz/react-native-design-system";
|
|
3
|
+
import { StepType } from "../../../../../domain/entities/flow-config.types";
|
|
12
4
|
import { GenericPhotoUploadScreen } from "../screens/GenericPhotoUploadScreen";
|
|
13
5
|
import { GeneratingScreen } from "../screens/GeneratingScreen";
|
|
14
6
|
import { ScenarioPreviewScreen } from "../../../../scenarios/presentation/screens/ScenarioPreviewScreen";
|
|
15
|
-
import type { ScenarioData } from "../../../../scenarios/domain/scenario.types";
|
|
16
7
|
import { ResultPreviewScreen } from "../../../../result-preview/presentation/components/ResultPreviewScreen";
|
|
17
|
-
import {
|
|
8
|
+
import { getWizardStepConfig, getUploadedImage } from "./WizardStepRenderer.utils";
|
|
9
|
+
import type { WizardStepRendererProps } from "./WizardStepRenderer.types";
|
|
18
10
|
|
|
19
|
-
export
|
|
20
|
-
readonly step: StepDefinition | undefined;
|
|
21
|
-
readonly scenario?: WizardScenarioData;
|
|
22
|
-
readonly customData: Record<string, unknown>;
|
|
23
|
-
readonly generationProgress: number;
|
|
24
|
-
readonly generationResult: unknown;
|
|
25
|
-
readonly isSaving: boolean;
|
|
26
|
-
readonly isSharing: boolean;
|
|
27
|
-
readonly onNext: () => void;
|
|
28
|
-
readonly onBack: () => void;
|
|
29
|
-
readonly onPhotoContinue: (stepId: string, image: UploadedImage) => void;
|
|
30
|
-
readonly onDownload: () => void;
|
|
31
|
-
readonly onShare: () => void;
|
|
32
|
-
readonly onTryAgain?: () => void;
|
|
33
|
-
readonly t: (key: string) => string;
|
|
34
|
-
readonly renderPreview?: (onContinue: () => void) => React.ReactElement | null;
|
|
35
|
-
readonly renderGenerating?: (progress: number) => React.ReactElement | null;
|
|
36
|
-
readonly renderResult?: (result: unknown) => React.ReactElement | null;
|
|
37
|
-
}
|
|
11
|
+
export type { WizardStepRendererProps } from "./WizardStepRenderer.types";
|
|
38
12
|
|
|
39
13
|
export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
40
14
|
step,
|
|
@@ -64,13 +38,11 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
|
64
38
|
|
|
65
39
|
switch (step.type) {
|
|
66
40
|
case StepType.SCENARIO_PREVIEW: {
|
|
67
|
-
if (renderPreview)
|
|
68
|
-
return renderPreview(onNext);
|
|
69
|
-
}
|
|
41
|
+
if (renderPreview) return renderPreview(onNext);
|
|
70
42
|
if (!scenario) return null;
|
|
71
43
|
return (
|
|
72
44
|
<ScenarioPreviewScreen
|
|
73
|
-
scenario={scenario
|
|
45
|
+
scenario={scenario}
|
|
74
46
|
translations={{
|
|
75
47
|
continueButton: t("common.continue"),
|
|
76
48
|
whatToExpect: t("scenarioPreview.whatToExpect"),
|
|
@@ -83,27 +55,19 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
|
83
55
|
}
|
|
84
56
|
|
|
85
57
|
case StepType.GENERATING: {
|
|
86
|
-
if (renderGenerating)
|
|
87
|
-
return renderGenerating(generationProgress);
|
|
88
|
-
}
|
|
58
|
+
if (renderGenerating) return renderGenerating(generationProgress);
|
|
89
59
|
return (
|
|
90
|
-
<GeneratingScreen
|
|
91
|
-
progress={generationProgress}
|
|
92
|
-
scenario={scenario}
|
|
93
|
-
t={t}
|
|
94
|
-
/>
|
|
60
|
+
<GeneratingScreen progress={generationProgress} scenario={scenario} t={t} />
|
|
95
61
|
);
|
|
96
62
|
}
|
|
97
63
|
|
|
98
64
|
case StepType.RESULT_PREVIEW: {
|
|
99
|
-
if (renderResult)
|
|
100
|
-
return renderResult(generationResult);
|
|
101
|
-
}
|
|
65
|
+
if (renderResult) return renderResult(generationResult);
|
|
102
66
|
const media = extractMediaUrl(generationResult);
|
|
103
67
|
if (!media) return null;
|
|
104
68
|
|
|
105
|
-
const
|
|
106
|
-
const
|
|
69
|
+
const isVideo = media.isVideo || getMediaTypeFromUrl(media.url) === "video";
|
|
70
|
+
const handleTryAgain = onTryAgain ?? onBack;
|
|
107
71
|
|
|
108
72
|
return (
|
|
109
73
|
<ResultPreviewScreen
|
|
@@ -113,8 +77,8 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
|
113
77
|
isSharing={isSharing}
|
|
114
78
|
onDownload={onDownload}
|
|
115
79
|
onShare={onShare}
|
|
116
|
-
onTryAgain={
|
|
117
|
-
onNavigateBack={
|
|
80
|
+
onTryAgain={handleTryAgain}
|
|
81
|
+
onNavigateBack={handleTryAgain}
|
|
118
82
|
translations={{
|
|
119
83
|
title: t("generation.result.title"),
|
|
120
84
|
yourResult: t("generation.result.yourResult"),
|
|
@@ -129,10 +93,10 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
|
129
93
|
}
|
|
130
94
|
|
|
131
95
|
case StepType.PARTNER_UPLOAD: {
|
|
132
|
-
const wizardConfig = step.config
|
|
133
|
-
const titleKey = wizardConfig?.titleKey
|
|
134
|
-
const subtitleKey = wizardConfig?.subtitleKey
|
|
135
|
-
const existingPhoto = customData[step.id]
|
|
96
|
+
const wizardConfig = getWizardStepConfig(step.config);
|
|
97
|
+
const titleKey = wizardConfig?.titleKey ?? `wizard.steps.${step.id}.title`;
|
|
98
|
+
const subtitleKey = wizardConfig?.subtitleKey ?? `wizard.steps.${step.id}.subtitle`;
|
|
99
|
+
const existingPhoto = getUploadedImage(customData[step.id]);
|
|
136
100
|
|
|
137
101
|
return (
|
|
138
102
|
<GenericPhotoUploadScreen
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { StepDefinition } from "../../../../../domain/entities/flow-config.types";
|
|
2
|
+
import type { WizardScenarioData } from "../hooks/useWizardGeneration";
|
|
3
|
+
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
4
|
+
|
|
5
|
+
export interface WizardStepRendererProps {
|
|
6
|
+
readonly step: StepDefinition | undefined;
|
|
7
|
+
readonly scenario?: WizardScenarioData;
|
|
8
|
+
readonly customData: Record<string, unknown>;
|
|
9
|
+
readonly generationProgress: number;
|
|
10
|
+
readonly generationResult: unknown;
|
|
11
|
+
readonly isSaving: boolean;
|
|
12
|
+
readonly isSharing: boolean;
|
|
13
|
+
readonly onNext: () => void;
|
|
14
|
+
readonly onBack: () => void;
|
|
15
|
+
readonly onPhotoContinue: (stepId: string, image: UploadedImage) => void;
|
|
16
|
+
readonly onDownload: () => void;
|
|
17
|
+
readonly onShare: () => void;
|
|
18
|
+
readonly onTryAgain?: () => void;
|
|
19
|
+
readonly t: (key: string) => string;
|
|
20
|
+
readonly renderPreview?: (onContinue: () => void) => React.ReactElement | null;
|
|
21
|
+
readonly renderGenerating?: (progress: number) => React.ReactElement | null;
|
|
22
|
+
readonly renderResult?: (result: unknown) => React.ReactElement | null;
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WizardStepConfig } from "../../domain/entities/wizard-config.types";
|
|
2
|
+
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
3
|
+
|
|
4
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
5
|
+
return typeof value === "object" && value !== null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isWizardStepConfig(value: unknown): value is WizardStepConfig {
|
|
9
|
+
return isRecord(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isUploadedImage(value: unknown): value is UploadedImage {
|
|
13
|
+
if (!isRecord(value)) return false;
|
|
14
|
+
return typeof value.uri === "string";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getWizardStepConfig(config: unknown): WizardStepConfig | undefined {
|
|
18
|
+
if (isWizardStepConfig(config)) return config;
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getUploadedImage(data: unknown): UploadedImage | undefined {
|
|
23
|
+
if (isUploadedImage(data)) return data;
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ResultPreviewScreen Component
|
|
3
|
-
* Displays AI generation result with actions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import React, { useMemo } from "react";
|
|
7
2
|
import { StyleSheet, View } from "react-native";
|
|
8
3
|
import {
|
|
@@ -16,6 +11,7 @@ import { ResultActionBar } from "./ResultActionBar";
|
|
|
16
11
|
import { RecentCreationsSection } from "./RecentCreationsSection";
|
|
17
12
|
import { VideoResultPlayer } from "../../../../presentation/components/display/VideoResultPlayer";
|
|
18
13
|
import type { ResultPreviewScreenProps } from "../types/result-preview.types";
|
|
14
|
+
import { formatMediaUrl, shouldShowRecentCreations } from "./ResultPreviewScreen.utils";
|
|
19
15
|
|
|
20
16
|
export const ResultPreviewScreen: React.FC<ResultPreviewScreenProps> = ({
|
|
21
17
|
imageUrl,
|
|
@@ -39,17 +35,16 @@ export const ResultPreviewScreen: React.FC<ResultPreviewScreenProps> = ({
|
|
|
39
35
|
}) => {
|
|
40
36
|
const tokens = useAppDesignTokens();
|
|
41
37
|
const isVideo = Boolean(videoUrl);
|
|
38
|
+
const displayMediaUrl = useMemo(
|
|
39
|
+
() => formatMediaUrl(videoUrl, imageUrl, isVideo),
|
|
40
|
+
[imageUrl, videoUrl, isVideo]
|
|
41
|
+
);
|
|
42
42
|
|
|
43
43
|
const styles = useMemo(
|
|
44
44
|
() =>
|
|
45
45
|
StyleSheet.create({
|
|
46
|
-
container: {
|
|
47
|
-
|
|
48
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
49
|
-
},
|
|
50
|
-
resultContainer: {
|
|
51
|
-
marginTop: tokens.spacing.lg,
|
|
52
|
-
},
|
|
46
|
+
container: { flex: 1, paddingHorizontal: tokens.spacing.lg },
|
|
47
|
+
resultContainer: { marginTop: tokens.spacing.lg },
|
|
53
48
|
title: {
|
|
54
49
|
fontSize: 18,
|
|
55
50
|
fontWeight: "700",
|
|
@@ -57,33 +52,20 @@ export const ResultPreviewScreen: React.FC<ResultPreviewScreenProps> = ({
|
|
|
57
52
|
marginBottom: tokens.spacing.md,
|
|
58
53
|
},
|
|
59
54
|
}),
|
|
60
|
-
[tokens]
|
|
55
|
+
[tokens]
|
|
61
56
|
);
|
|
62
57
|
|
|
63
|
-
const displayMediaUrl = useMemo(() => {
|
|
64
|
-
const url = videoUrl || imageUrl;
|
|
65
|
-
if (!url) return null;
|
|
66
|
-
if (!isVideo && !url.startsWith("http") && !url.startsWith("data:image")) {
|
|
67
|
-
return `data:image/jpeg;base64,${url}`;
|
|
68
|
-
}
|
|
69
|
-
return url;
|
|
70
|
-
}, [imageUrl, videoUrl, isVideo]);
|
|
71
|
-
|
|
72
58
|
if (!displayMediaUrl) return null;
|
|
73
59
|
|
|
60
|
+
const showRecent = shouldShowRecentCreations(recentCreations, translations);
|
|
61
|
+
|
|
74
62
|
return (
|
|
75
63
|
<ScreenLayout scrollable edges={["left", "right"]} backgroundColor={tokens.colors.backgroundPrimary}>
|
|
76
64
|
<NavigationHeader title={translations.title} onBackPress={onNavigateBack} />
|
|
77
65
|
<View style={[styles.container, style]}>
|
|
78
66
|
<View style={styles.resultContainer}>
|
|
79
|
-
{!hideLabel &&
|
|
80
|
-
|
|
81
|
-
)}
|
|
82
|
-
{isVideo ? (
|
|
83
|
-
<VideoResultPlayer uri={displayMediaUrl} />
|
|
84
|
-
) : (
|
|
85
|
-
<ResultImageCard imageUrl={displayMediaUrl} />
|
|
86
|
-
)}
|
|
67
|
+
{!hideLabel && <AtomicText style={styles.title}>{translations.yourResult}</AtomicText>}
|
|
68
|
+
{isVideo ? <VideoResultPlayer uri={displayMediaUrl} /> : <ResultImageCard imageUrl={displayMediaUrl} />}
|
|
87
69
|
<ResultActionBar
|
|
88
70
|
isSaving={isSaving}
|
|
89
71
|
isSharing={isSharing}
|
|
@@ -99,13 +81,13 @@ export const ResultPreviewScreen: React.FC<ResultPreviewScreenProps> = ({
|
|
|
99
81
|
showRating={showRating}
|
|
100
82
|
/>
|
|
101
83
|
</View>
|
|
102
|
-
{
|
|
84
|
+
{showRecent && (
|
|
103
85
|
<RecentCreationsSection
|
|
104
|
-
recentCreations={recentCreations}
|
|
86
|
+
recentCreations={recentCreations!}
|
|
105
87
|
onViewAll={onViewAll}
|
|
106
88
|
onCreationPress={onCreationPress}
|
|
107
|
-
title={translations.recentCreations}
|
|
108
|
-
viewAllLabel={translations.viewAll}
|
|
89
|
+
title={translations.recentCreations!}
|
|
90
|
+
viewAllLabel={translations.viewAll!}
|
|
109
91
|
/>
|
|
110
92
|
)}
|
|
111
93
|
</View>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RecentCreation, ResultPreviewTranslations } from "../types/result-preview.types";
|
|
2
|
+
|
|
3
|
+
export function formatMediaUrl(
|
|
4
|
+
videoUrl: string | undefined,
|
|
5
|
+
imageUrl: string | undefined,
|
|
6
|
+
isVideo: boolean
|
|
7
|
+
): string | null {
|
|
8
|
+
const url = videoUrl ?? imageUrl;
|
|
9
|
+
if (!url) return null;
|
|
10
|
+
|
|
11
|
+
if (!isVideo && !url.startsWith("http") && !url.startsWith("data:image")) {
|
|
12
|
+
return `data:image/jpeg;base64,${url}`;
|
|
13
|
+
}
|
|
14
|
+
return url;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function shouldShowRecentCreations(
|
|
18
|
+
recentCreations: readonly RecentCreation[] | undefined,
|
|
19
|
+
translations: ResultPreviewTranslations
|
|
20
|
+
): boolean {
|
|
21
|
+
return Boolean(
|
|
22
|
+
recentCreations &&
|
|
23
|
+
recentCreations.length > 0 &&
|
|
24
|
+
translations.recentCreations &&
|
|
25
|
+
translations.viewAll
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Media URL Extractor
|
|
3
|
-
* Extracts media URL from generation result object
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
interface MediaUrlResult {
|
|
7
|
-
readonly url: string;
|
|
8
|
-
readonly isVideo: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Extract media URL from generation result
|
|
13
|
-
* Supports multiple result formats from different AI providers
|
|
14
|
-
*/
|
|
15
|
-
export function extractMediaUrl(result: unknown): MediaUrlResult | null {
|
|
16
|
-
if (!result || typeof result !== "object") return null;
|
|
17
|
-
|
|
18
|
-
const data = result as Record<string, unknown>;
|
|
19
|
-
const output = data.output as Record<string, unknown> | undefined;
|
|
20
|
-
|
|
21
|
-
// Try to get video URL first
|
|
22
|
-
const videoUrl = output?.videoUrl || data.videoUrl || data.video_url;
|
|
23
|
-
if (videoUrl && typeof videoUrl === "string") {
|
|
24
|
-
return { url: videoUrl, isVideo: true };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Try to get image URL
|
|
28
|
-
const imageUrl = output?.imageUrl || data.imageUrl || data.image_url || data.uri;
|
|
29
|
-
if (imageUrl && typeof imageUrl === "string") {
|
|
30
|
-
return { url: imageUrl, isVideo: false };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return null;
|
|
34
|
-
}
|