@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.26.56",
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, type StepDefinition } from "../../../../../domain/entities/flow-config.types";
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 { extractMediaUrl } from "../../infrastructure/utils/media-url-extractor";
8
+ import { getWizardStepConfig, getUploadedImage } from "./WizardStepRenderer.utils";
9
+ import type { WizardStepRendererProps } from "./WizardStepRenderer.types";
18
10
 
19
- export interface WizardStepRendererProps {
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 as unknown as ScenarioData}
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 mediaType = getMediaTypeFromUrl(media.url);
106
- const isVideo = media.isVideo || mediaType === "video";
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={onTryAgain || onBack}
117
- onNavigateBack={onTryAgain || onBack}
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 as WizardStepConfig;
133
- const titleKey = wizardConfig?.titleKey || `wizard.steps.${step.id}.title`;
134
- const subtitleKey = wizardConfig?.subtitleKey || `wizard.steps.${step.id}.subtitle`;
135
- const existingPhoto = customData[step.id] as UploadedImage | undefined;
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
- flex: 1,
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
- <AtomicText style={styles.title}>{translations.yourResult}</AtomicText>
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
- {recentCreations && recentCreations.length > 0 && translations.recentCreations && translations.viewAll && (
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
- }