@umituz/react-native-ai-generation-content 1.13.1 → 1.15.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.13.1",
3
+ "version": "1.15.0",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -67,7 +67,7 @@
67
67
  "@typescript-eslint/eslint-plugin": "^7.0.0",
68
68
  "@typescript-eslint/parser": "^7.0.0",
69
69
  "@umituz/react-native-animation": "*",
70
- "@umituz/react-native-design-system": "^2.3.33",
70
+ "@umituz/react-native-design-system": "^2.4.1",
71
71
  "@umituz/react-native-firebase": "^1.13.20",
72
72
  "@umituz/react-native-haptics": "^1.0.2",
73
73
  "@umituz/react-native-image": "*",
@@ -7,7 +7,6 @@ import {
7
7
  } from "@umituz/react-native-design-system";
8
8
  import { timezoneService } from "@umituz/react-native-timezone";
9
9
  import type { Creation } from "../../domain/entities/Creation";
10
- import type { CreationType } from "../../domain/value-objects/CreationsConfig";
11
10
 
12
11
  import { useCreationsProvider } from "./CreationsProvider";
13
12
 
@@ -29,7 +28,7 @@ export function CreationCard({
29
28
  locale = "en-US",
30
29
  }: CreationCardProps) {
31
30
  const tokens = useAppDesignTokens();
32
- const { translatedTypes, t } = useCreationsProvider();
31
+ const { translatedTypes } = useCreationsProvider();
33
32
 
34
33
  const typeConfig = translatedTypes.find((type) => type.id === creation.type);
35
34
  const icon = typeConfig?.icon;
@@ -2,7 +2,6 @@ import React from 'react';
2
2
  import { FlatList, RefreshControl, StyleSheet, type ViewStyle } from 'react-native';
3
3
  import { useAppDesignTokens, type DesignTokens } from "@umituz/react-native-design-system";
4
4
  import type { Creation } from "../../domain/entities/Creation";
5
- import type { CreationType } from "../../domain/value-objects/CreationsConfig";
6
5
  import { CreationCard } from "./CreationCard";
7
6
 
8
7
  interface CreationsGridProps {
@@ -1,7 +1,6 @@
1
1
  declare const __DEV__: boolean;
2
2
 
3
3
  import React, { useMemo, useCallback, useState } from "react";
4
- import { View, StyleSheet, type LayoutChangeEvent } from "react-native";
5
4
  import {
6
5
  useAppDesignTokens,
7
6
  useAlert,
@@ -9,17 +8,20 @@ import {
9
8
  AlertMode,
10
9
  useSharing,
11
10
  FilterBottomSheet,
12
- type DesignTokens,
13
11
  type BottomSheetModalRef,
14
- ScreenLayout
12
+ ScreenLayout,
15
13
  } from "@umituz/react-native-design-system";
16
- import { useSafeAreaInsets } from "react-native-safe-area-context";
17
14
  import { useFocusEffect } from "@react-navigation/native";
18
15
  import { useCreations } from "../hooks/useCreations";
19
16
  import { useDeleteCreation } from "../hooks/useDeleteCreation";
20
17
  import { useCreationsFilter } from "../hooks/useCreationsFilter";
21
- import { GalleryHeader, CreationsGrid, CreationImageViewer, GalleryEmptyStates } from "../components";
22
- import { getTranslatedTypes, getFilterCategoriesFromConfig } from "../utils/filterUtils";
18
+ import {
19
+ GalleryHeader,
20
+ CreationsGrid,
21
+ CreationImageViewer,
22
+ GalleryEmptyStates,
23
+ } from "../components";
24
+ import { getFilterCategoriesFromConfig } from "../utils/filterUtils";
23
25
  import type { Creation } from "../../domain/entities/Creation";
24
26
  import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
25
27
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
@@ -61,7 +63,6 @@ function CreationsGalleryScreenContent({
61
63
  showFilter = config.showFilter ?? true,
62
64
  }: CreationsGalleryScreenProps) {
63
65
  const tokens = useAppDesignTokens();
64
- const insets = useSafeAreaInsets();
65
66
  const { share } = useSharing();
66
67
  const alert = useAlert();
67
68
 
@@ -83,8 +84,10 @@ function CreationsGalleryScreenContent({
83
84
  );
84
85
 
85
86
  // Prepare data for UI using utils
86
- const translatedTypes = useMemo(() => getTranslatedTypes(config, t), [config, t]);
87
- const allCategories = useMemo(() => getFilterCategoriesFromConfig(config, t), [config, t]);
87
+ const allCategories = useMemo(
88
+ () => getFilterCategoriesFromConfig(config, t),
89
+ [config, t],
90
+ );
88
91
 
89
92
  const handleShare = useCallback((creation: Creation) => {
90
93
  void share(creation.uri, { dialogTitle: t("common.share") });
@@ -124,15 +127,17 @@ function CreationsGalleryScreenContent({
124
127
  const handleFavorite = useCallback((creation: Creation, isFavorite: boolean) => {
125
128
  void (async () => {
126
129
  if (!userId) return;
127
- const success = await repository.updateFavorite(userId, creation.id, isFavorite);
130
+ const success = await repository.updateFavorite(
131
+ userId,
132
+ creation.id,
133
+ isFavorite,
134
+ );
128
135
  if (success) {
129
136
  void refetch();
130
137
  }
131
138
  })();
132
139
  }, [userId, repository, refetch]);
133
140
 
134
- const styles = useStyles(tokens);
135
-
136
141
  const renderEmptyComponent = useMemo(() => (
137
142
  <GalleryEmptyStates
138
143
  isLoading={isLoading}
@@ -228,13 +233,3 @@ function CreationsGalleryScreenContent({
228
233
  );
229
234
  }
230
235
 
231
- const useStyles = (tokens: DesignTokens) => StyleSheet.create({
232
- container: { flex: 1, backgroundColor: tokens.colors.background },
233
- centerContainer: {
234
- flex: 1,
235
- justifyContent: 'center',
236
- alignItems: 'center',
237
- minHeight: 400,
238
- paddingHorizontal: tokens.spacing.xl
239
- },
240
- });
package/src/index.ts CHANGED
@@ -186,6 +186,7 @@ export {
186
186
  usePendingJobs,
187
187
  useBackgroundGeneration,
188
188
  usePhotoGeneration,
189
+ useGenerationFlow,
189
190
  } from "./presentation/hooks";
190
191
 
191
192
  export type {
@@ -203,6 +204,8 @@ export type {
203
204
  PhotoGenerationConfig,
204
205
  PhotoGenerationState,
205
206
  PhotoGenerationStatus,
207
+ UseGenerationFlowOptions,
208
+ UseGenerationFlowReturn,
206
209
  } from "./presentation/hooks";
207
210
 
208
211
  // =============================================================================
@@ -221,6 +224,8 @@ export {
221
224
  ResultImageCard,
222
225
  ResultStoryCard,
223
226
  ResultActions,
227
+ DEFAULT_RESULT_CONFIG,
228
+ PhotoStep,
224
229
  } from "./presentation/components";
225
230
 
226
231
  export type {
@@ -238,8 +243,35 @@ export type {
238
243
  ResultImageCardProps,
239
244
  ResultStoryCardProps,
240
245
  ResultActionsProps,
246
+ ResultConfig,
247
+ ResultHeaderConfig,
248
+ ResultImageConfig,
249
+ ResultStoryConfig,
250
+ ResultActionsConfig,
251
+ ResultLayoutConfig,
252
+ ResultActionButton,
253
+ PhotoStepProps,
241
254
  } from "./presentation/components";
242
255
 
256
+ // =============================================================================
257
+ // PRESENTATION LAYER - Flow Configuration
258
+ // =============================================================================
259
+
260
+ export {
261
+ DEFAULT_SINGLE_PHOTO_FLOW,
262
+ DEFAULT_DUAL_PHOTO_FLOW,
263
+ } from "./presentation/types/flow-config.types";
264
+
265
+ export type {
266
+ PhotoStepConfig,
267
+ TextInputStepConfig,
268
+ PreviewStepConfig,
269
+ GenerationFlowConfig,
270
+ PhotoStepData,
271
+ TextInputStepData,
272
+ GenerationFlowState,
273
+ } from "./presentation/types/flow-config.types";
274
+
243
275
  // =============================================================================
244
276
  // DOMAINS - AI Prompts
245
277
  // =============================================================================
@@ -22,3 +22,4 @@ export type { PendingJobProgressBarProps } from "./PendingJobProgressBar";
22
22
  export type { PendingJobCardActionsProps } from "./PendingJobCardActions";
23
23
 
24
24
  export * from "./result";
25
+ export * from "./photo-step";
@@ -0,0 +1,96 @@
1
+ /**
2
+ * PhotoStep Component
3
+ * Configurable photo upload step using design system components
4
+ *
5
+ * @package @umituz/react-native-ai-generation-content
6
+ */
7
+
8
+ import React, { useMemo } from "react";
9
+ import { View, StyleSheet, type ViewStyle, type StyleProp } from "react-native";
10
+ import {
11
+ StepHeader,
12
+ PhotoUploadCard,
13
+ } from "@umituz/react-native-design-system";
14
+ import type { PhotoStepConfig } from "../../types/flow-config.types";
15
+
16
+ export interface PhotoStepProps {
17
+ /** Step configuration */
18
+ config: PhotoStepConfig;
19
+ /** Current photo URI */
20
+ imageUri: string | null;
21
+ /** Photo preview URL */
22
+ previewUrl?: string;
23
+ /** Whether photo is being validated */
24
+ isValidating?: boolean;
25
+ /** Whether photo is valid */
26
+ isValid?: boolean | null;
27
+ /** Handler for photo selection */
28
+ onPhotoSelect: () => void;
29
+ /** Whether photo selection is disabled */
30
+ disabled?: boolean;
31
+ /** Step title */
32
+ title: string;
33
+ /** Step subtitle */
34
+ subtitle?: string;
35
+ /** Translation strings for photo upload card */
36
+ translations: {
37
+ tapToUpload: string;
38
+ selectPhoto: string;
39
+ change: string;
40
+ analyzing?: string;
41
+ };
42
+ /** Additional content to render below photo card */
43
+ children?: React.ReactNode;
44
+ /** Container style */
45
+ style?: StyleProp<ViewStyle>;
46
+ }
47
+
48
+ export const PhotoStep: React.FC<PhotoStepProps> = ({
49
+ config,
50
+ imageUri,
51
+ previewUrl,
52
+ isValidating = false,
53
+ isValid = null,
54
+ onPhotoSelect,
55
+ disabled = false,
56
+ title,
57
+ subtitle,
58
+ translations,
59
+ children,
60
+ style,
61
+ }) => {
62
+ const styles = useMemo(
63
+ () =>
64
+ StyleSheet.create({
65
+ container: {
66
+ flex: 1,
67
+ },
68
+ }),
69
+ [],
70
+ );
71
+
72
+ return (
73
+ <View style={[styles.container, style]}>
74
+ {/* Step Header */}
75
+ <StepHeader
76
+ title={title}
77
+ subtitle={subtitle}
78
+ config={config.header}
79
+ />
80
+
81
+ {/* Photo Upload Card */}
82
+ <PhotoUploadCard
83
+ imageUri={previewUrl || imageUri}
84
+ onPress={onPhotoSelect}
85
+ isValidating={isValidating}
86
+ isValid={config.enableValidation ? isValid : null}
87
+ disabled={disabled}
88
+ config={config.photoCard}
89
+ translations={translations}
90
+ />
91
+
92
+ {/* Additional content (e.g., name input, tips, etc.) */}
93
+ {children}
94
+ </View>
95
+ );
96
+ };
@@ -0,0 +1,2 @@
1
+ export { PhotoStep } from "./PhotoStep";
2
+ export type { PhotoStepProps } from "./PhotoStep";
@@ -1,16 +1,28 @@
1
1
  /**
2
2
  * GenerationResultContent Component
3
- * Composition of result components for CelebrationModal
3
+ * Composition of result components for CelebrationModal - fully configurable
4
4
  */
5
5
 
6
6
  import * as React from "react";
7
7
  import { useMemo } from "react";
8
- import { ScrollView, StyleSheet, Dimensions, type ViewStyle, type StyleProp } from "react-native";
9
- import { Animated, useAppDesignTokens } from "@umituz/react-native-design-system";
8
+ import {
9
+ ScrollView,
10
+ StyleSheet,
11
+ Dimensions,
12
+ type ViewStyle,
13
+ type StyleProp,
14
+ type DimensionValue,
15
+ } from "react-native";
16
+ import {
17
+ Animated,
18
+ useAppDesignTokens,
19
+ } from "@umituz/react-native-design-system";
10
20
  import { ResultHeader } from "./ResultHeader";
11
21
  import { ResultImageCard } from "./ResultImageCard";
12
22
  import { ResultStoryCard } from "./ResultStoryCard";
13
23
  import { ResultActions } from "./ResultActions";
24
+ import type { ResultConfig } from "../../types/result-config.types";
25
+ import { DEFAULT_RESULT_CONFIG } from "../../types/result-config.types";
14
26
 
15
27
  const { width } = Dimensions.get("window");
16
28
 
@@ -36,9 +48,12 @@ export interface GenerationResultContentProps {
36
48
  aiGenerated: string;
37
49
  };
38
50
  modalStyle?: StyleProp<ViewStyle>;
51
+ config?: ResultConfig;
39
52
  }
40
53
 
41
- export const GenerationResultContent: React.FC<GenerationResultContentProps> = ({
54
+ export const GenerationResultContent: React.FC<
55
+ GenerationResultContentProps
56
+ > = ({
42
57
  result,
43
58
  onShare,
44
59
  onSave,
@@ -47,25 +62,35 @@ export const GenerationResultContent: React.FC<GenerationResultContentProps> = (
47
62
  isSaving,
48
63
  translations,
49
64
  modalStyle,
65
+ config = DEFAULT_RESULT_CONFIG,
50
66
  }) => {
51
67
  const tokens = useAppDesignTokens();
68
+ const cfg = { ...DEFAULT_RESULT_CONFIG, ...config };
52
69
 
53
- const styles = useMemo(() => StyleSheet.create({
54
- container: {
55
- width: width - 40,
56
- maxHeight: "90%",
57
- backgroundColor: tokens.colors.background,
58
- borderRadius: 28,
59
- overflow: "hidden",
60
- },
61
- scrollView: {
62
- flex: 1,
63
- },
64
- scrollContent: {
65
- paddingTop: 24,
66
- paddingBottom: 20,
67
- },
68
- }), [tokens]);
70
+ const styles = useMemo(() => {
71
+ const containerWidth = cfg.layout?.maxWidth ?? width - 40;
72
+ const maxHeight: DimensionValue = (cfg.layout?.maxHeight ??
73
+ "90%") as DimensionValue;
74
+
75
+ return StyleSheet.create({
76
+ container: {
77
+ width: containerWidth,
78
+ maxHeight,
79
+ backgroundColor:
80
+ cfg.layout?.backgroundColor ?? tokens.colors.background,
81
+ borderRadius: cfg.layout?.borderRadius ?? 28,
82
+ overflow: "hidden",
83
+ },
84
+ scrollView: {
85
+ flex: 1,
86
+ },
87
+ scrollContent: {
88
+ paddingTop: cfg.layout?.contentPadding?.top ?? 24,
89
+ paddingBottom: cfg.layout?.contentPadding?.bottom ?? 20,
90
+ paddingHorizontal: cfg.layout?.contentPadding?.horizontal ?? 0,
91
+ },
92
+ });
93
+ }, [tokens, cfg, width]);
69
94
 
70
95
  return (
71
96
  <Animated.View style={[styles.container, modalStyle]}>
@@ -73,10 +98,21 @@ export const GenerationResultContent: React.FC<GenerationResultContentProps> = (
73
98
  style={styles.scrollView}
74
99
  contentContainerStyle={styles.scrollContent}
75
100
  showsVerticalScrollIndicator={false}
101
+ scrollEnabled={cfg.layout?.scrollEnabled ?? true}
76
102
  >
77
- <ResultHeader title={result.title} date={result.date} />
78
- <ResultImageCard imageUrl={result.imageUrl} badgeText={translations.aiGenerated} />
79
- {result.story && <ResultStoryCard story={result.story} />}
103
+ <ResultHeader
104
+ title={result.title}
105
+ date={result.date}
106
+ config={cfg.header}
107
+ />
108
+ <ResultImageCard
109
+ imageUrl={result.imageUrl}
110
+ badgeText={translations.aiGenerated}
111
+ config={cfg.image}
112
+ />
113
+ {result.story && (
114
+ <ResultStoryCard story={result.story} config={cfg.story} />
115
+ )}
80
116
  <ResultActions
81
117
  onShare={onShare}
82
118
  onSave={onSave}
@@ -84,6 +120,7 @@ export const GenerationResultContent: React.FC<GenerationResultContentProps> = (
84
120
  isSharing={isSharing}
85
121
  isSaving={isSaving}
86
122
  translations={translations}
123
+ config={cfg.actions}
87
124
  />
88
125
  </ScrollView>
89
126
  </Animated.View>