@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 +2 -2
- package/src/domains/creations/presentation/components/CreationCard.tsx +1 -2
- package/src/domains/creations/presentation/components/CreationsGrid.tsx +0 -1
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +17 -22
- package/src/index.ts +32 -0
- package/src/presentation/components/index.ts +1 -0
- package/src/presentation/components/photo-step/PhotoStep.tsx +96 -0
- package/src/presentation/components/photo-step/index.ts +2 -0
- package/src/presentation/components/result/GenerationResultContent.tsx +60 -23
- package/src/presentation/components/result/ResultActions.tsx +171 -84
- package/src/presentation/components/result/ResultHeader.tsx +69 -38
- package/src/presentation/components/result/ResultImageCard.tsx +118 -39
- package/src/presentation/components/result/ResultStoryCard.tsx +94 -44
- package/src/presentation/components/result/index.ts +14 -0
- package/src/presentation/hooks/index.ts +6 -0
- package/src/presentation/hooks/useGenerationFlow.ts +315 -0
- package/src/presentation/types/flow-config.types.ts +246 -0
- package/src/presentation/types/result-config.types.ts +194 -0
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.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.
|
|
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
|
|
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 {
|
|
22
|
-
|
|
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
|
|
87
|
-
|
|
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(
|
|
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
|
// =============================================================================
|
|
@@ -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
|
+
};
|
|
@@ -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 {
|
|
9
|
-
|
|
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<
|
|
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(() =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
78
|
-
|
|
79
|
-
|
|
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>
|