@umituz/react-native-ai-generation-content 1.26.14 → 1.26.16

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.
Files changed (33) hide show
  1. package/package.json +1 -1
  2. package/src/domains/love-message/domain/constants.ts +162 -0
  3. package/src/domains/love-message/domain/types.ts +58 -0
  4. package/src/domains/love-message/index.ts +37 -0
  5. package/src/domains/love-message/infrastructure/persistence/PartnerProfileRepository.ts +52 -0
  6. package/src/domains/love-message/infrastructure/prompts/messagePromptBuilder.ts +109 -0
  7. package/src/domains/love-message/infrastructure/services/LoveMessageService.ts +35 -0
  8. package/src/domains/love-message/presentation/components/CategoryGrid.tsx +88 -0
  9. package/src/domains/love-message/presentation/components/DetailsInput.tsx +74 -0
  10. package/src/domains/love-message/presentation/components/ExploreHeader.tsx +67 -0
  11. package/src/domains/love-message/presentation/components/FieldInput.tsx +83 -0
  12. package/src/domains/love-message/presentation/components/GeneratorHeader.tsx +88 -0
  13. package/src/domains/love-message/presentation/components/LoveMessageHeroSection.tsx +114 -0
  14. package/src/domains/love-message/presentation/components/MessageListItem.tsx +77 -0
  15. package/src/domains/love-message/presentation/components/MessageResult.tsx +105 -0
  16. package/src/domains/love-message/presentation/components/PartnerInput.tsx +78 -0
  17. package/src/domains/love-message/presentation/components/ProgressDots.tsx +48 -0
  18. package/src/domains/love-message/presentation/components/StepDetails.tsx +23 -0
  19. package/src/domains/love-message/presentation/components/StepPartner.tsx +116 -0
  20. package/src/domains/love-message/presentation/components/StepVibe.tsx +30 -0
  21. package/src/domains/love-message/presentation/components/ToneSelector.tsx +100 -0
  22. package/src/domains/love-message/presentation/components/TrendingSection.tsx +130 -0
  23. package/src/domains/love-message/presentation/components/TypeSelector.tsx +99 -0
  24. package/src/domains/love-message/presentation/hooks/useLoveMessageGenerator.ts +114 -0
  25. package/src/domains/love-message/presentation/hooks/usePartnerProfile.ts +43 -0
  26. package/src/domains/love-message/presentation/navigation/LoveMessageStack.tsx +39 -0
  27. package/src/domains/love-message/presentation/screens/LoveMessageExploreScreen.tsx +53 -0
  28. package/src/domains/love-message/presentation/screens/LoveMessageGeneratorScreen.tsx +169 -0
  29. package/src/domains/love-message/presentation/screens/MessageListScreen.tsx +127 -0
  30. package/src/domains/love-message/presentation/screens/PartnerProfileScreen.tsx +119 -0
  31. package/src/domains/scenarios/domain/scenario.types.ts +49 -0
  32. package/src/domains/scenarios/index.ts +5 -0
  33. package/src/index.ts +1 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Love Message Explore Screen
3
+ * Premium entry point for Love Message domain
4
+ */
5
+
6
+ import { FC } from "react";
7
+ import { View, ScrollView, StyleSheet } from "react-native";
8
+ import {
9
+ useAppDesignTokens,
10
+ useSafeAreaInsets,
11
+ useAppNavigation,
12
+ } from "@umituz/react-native-design-system";
13
+ import { ExploreHeader } from "../components/ExploreHeader";
14
+ import { LoveMessageHeroSection } from "../components/LoveMessageHeroSection";
15
+ import { CategoryGrid } from "../components/CategoryGrid";
16
+ import { TrendingSection } from "../components/TrendingSection";
17
+
18
+ export const LoveMessageExploreScreen: FC = () => {
19
+ const tokens = useAppDesignTokens();
20
+ const { bottom } = useSafeAreaInsets();
21
+ const navigation = useAppNavigation();
22
+
23
+ const handleNavigateToGenerator = () => {
24
+ navigation.navigate("MessageGenerator", {});
25
+ };
26
+
27
+ const handleNavigateToCategory = (categoryId: string) => {
28
+ navigation.navigate("MessageList", { categoryId });
29
+ };
30
+
31
+ const handleNavigateToTrending = () => {
32
+ navigation.navigate("MessageList", { categoryId: "trending" });
33
+ };
34
+
35
+ return (
36
+ <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
37
+ <ExploreHeader onMagicPress={handleNavigateToGenerator} />
38
+
39
+ <ScrollView
40
+ showsVerticalScrollIndicator={false}
41
+ contentContainerStyle={{ paddingBottom: bottom + 120 }}
42
+ >
43
+ <LoveMessageHeroSection />
44
+ <CategoryGrid onCategoryPress={handleNavigateToCategory} />
45
+ <TrendingSection onViewAll={handleNavigateToTrending} />
46
+ </ScrollView>
47
+ </View>
48
+ );
49
+ };
50
+
51
+ const styles = StyleSheet.create({
52
+ container: { flex: 1 },
53
+ });
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Love Message Generator Screen
3
+ * Multi-step wizard flow
4
+ */
5
+
6
+ import { FC, useMemo, useCallback } from "react";
7
+ import { View, ScrollView, StyleSheet } from "react-native";
8
+ import {
9
+ AtomicText,
10
+ AtomicButton,
11
+ useAppNavigation,
12
+ useAppRoute,
13
+ AlertService,
14
+ useAppDesignTokens,
15
+ useSafeAreaInsets,
16
+ } from "@umituz/react-native-design-system";
17
+
18
+ import { useLocalization } from "@umituz/react-native-localization";
19
+ import { ProgressDots } from "../components/ProgressDots";
20
+ import { MessageResult } from "../components/MessageResult";
21
+ import { GeneratorHeader } from "../components/GeneratorHeader";
22
+ import { StepPartner } from "../components/StepPartner";
23
+ import { StepVibe } from "../components/StepVibe";
24
+ import { StepDetails } from "../components/StepDetails";
25
+ import { MessageType } from "../../domain/types";
26
+ import { useLoveMessageGenerator, GeneratorStep } from "../hooks/useLoveMessageGenerator";
27
+
28
+ type RouteParams = { initialType?: MessageType };
29
+
30
+ export const LoveMessageGeneratorScreen: FC = () => {
31
+ const tokens = useAppDesignTokens();
32
+ const { bottom } = useSafeAreaInsets();
33
+ const { t } = useLocalization();
34
+ const navigation = useAppNavigation();
35
+ const route = useAppRoute<{ params: RouteParams }, "params">();
36
+
37
+ const initialType = route.params?.initialType;
38
+
39
+ const handleSuccess = useCallback(() => {
40
+ AlertService.createSuccessAlert(
41
+ t("loveMessage.generator.successTitle"),
42
+ t("loveMessage.generator.successMessage")
43
+ );
44
+ }, [t]);
45
+
46
+ const handleError = useCallback(() => {
47
+ AlertService.createErrorAlert(
48
+ t("loveMessage.generator.errorTitle"),
49
+ t("loveMessage.generator.errorMessage")
50
+ );
51
+ }, [t]);
52
+
53
+ const gen = useLoveMessageGenerator({
54
+ onBack: () => navigation.goBack(),
55
+ initialType,
56
+ onSuccess: handleSuccess,
57
+ onError: handleError,
58
+ });
59
+
60
+ const handleNavigateToProfile = () => navigation.navigate("PartnerProfile");
61
+
62
+ const stepTitle = useMemo(() => {
63
+ switch (gen.currentStep) {
64
+ case GeneratorStep.PARTNER:
65
+ return t("loveMessage.generator.stepPartner");
66
+ case GeneratorStep.VIBE:
67
+ return t("loveMessage.generator.stepVibe");
68
+ case GeneratorStep.DETAILS:
69
+ return t("loveMessage.generator.stepDetails");
70
+ case GeneratorStep.RESULT:
71
+ return t("loveMessage.generator.stepResult");
72
+ default:
73
+ return "";
74
+ }
75
+ }, [gen.currentStep, t]);
76
+
77
+ return (
78
+ <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
79
+ <GeneratorHeader
80
+ currentStep={gen.currentStep}
81
+ partnerName={gen.partnerName}
82
+ onBack={gen.handleBack}
83
+ onNext={gen.handleNext}
84
+ onGenerate={gen.handleGenerate}
85
+ isGenerating={gen.isGenerating}
86
+ />
87
+
88
+ <ScrollView
89
+ showsVerticalScrollIndicator={false}
90
+ contentContainerStyle={{ paddingBottom: bottom + 120 }}
91
+ >
92
+ <View style={styles.heroSection}>
93
+ <ProgressDots currentStep={gen.currentStep} totalSteps={4} />
94
+ <AtomicText type="headlineLarge" color="textPrimary" style={styles.heroTitle}>
95
+ {stepTitle}
96
+ </AtomicText>
97
+ </View>
98
+
99
+ <View style={styles.formContent}>
100
+ {gen.currentStep === GeneratorStep.PARTNER && (
101
+ <View>
102
+ <StepPartner
103
+ partnerName={gen.partnerName}
104
+ setPartnerName={gen.setPartnerName}
105
+ useProfile={gen.useProfile}
106
+ setUseProfile={gen.setUseProfile}
107
+ hasProfile={gen.hasProfile}
108
+ onEditProfile={handleNavigateToProfile}
109
+ />
110
+ </View>
111
+ )}
112
+
113
+ {gen.currentStep === GeneratorStep.VIBE && (
114
+ <View>
115
+ <StepVibe
116
+ selectedType={gen.selectedType}
117
+ setSelectedType={gen.setSelectedType}
118
+ selectedTone={gen.selectedTone}
119
+ setSelectedTone={gen.setSelectedTone}
120
+ />
121
+ </View>
122
+ )}
123
+
124
+ {gen.currentStep === GeneratorStep.DETAILS && (
125
+ <View>
126
+ <StepDetails details={gen.details} setDetails={gen.setDetails} />
127
+ </View>
128
+ )}
129
+
130
+ {gen.currentStep === GeneratorStep.RESULT && (
131
+ <MessageResult message={gen.generatedMessage} />
132
+ )}
133
+ </View>
134
+ </ScrollView>
135
+
136
+ {gen.currentStep === GeneratorStep.RESULT && (
137
+ <View style={[styles.footer, { paddingBottom: bottom + 24 }]}>
138
+ <AtomicButton
139
+ title={t("loveMessage.generator.startOver")}
140
+ onPress={gen.startOver}
141
+ variant="outline"
142
+ fullWidth
143
+ style={styles.actionBtn}
144
+ />
145
+ </View>
146
+ )}
147
+ </View>
148
+ );
149
+ };
150
+
151
+ const styles = StyleSheet.create({
152
+ container: { flex: 1 },
153
+ heroSection: { paddingVertical: 32, alignItems: "center" },
154
+ heroTitle: {
155
+ fontWeight: "bold",
156
+ textAlign: "center",
157
+ marginBottom: 8,
158
+ paddingHorizontal: 20,
159
+ },
160
+ formContent: { paddingHorizontal: 20 },
161
+ footer: {
162
+ position: "absolute",
163
+ bottom: 0,
164
+ left: 0,
165
+ right: 0,
166
+ padding: 24,
167
+ },
168
+ actionBtn: { height: 60, borderRadius: 30 },
169
+ });
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Message List Screen
3
+ */
4
+
5
+ import { FC, useCallback, useMemo } from "react";
6
+ import { View, ScrollView, StyleSheet, Pressable, Share } from "react-native";
7
+ import {
8
+ AtomicText,
9
+ AtomicIcon,
10
+ useAppNavigation,
11
+ useAppRoute,
12
+ useAppDesignTokens,
13
+ useSafeAreaInsets,
14
+ } from "@umituz/react-native-design-system";
15
+ import { useLocalization } from "@umituz/react-native-localization";
16
+ import { CATEGORY_TEMPLATES, MESSAGE_TYPES } from "../../domain/constants";
17
+ import { MessageListItem } from "../components/MessageListItem";
18
+ import type { MessageType } from "../../domain/types";
19
+
20
+ type RouteParams = { categoryId?: string };
21
+
22
+ export const MessageListScreen: FC = () => {
23
+ const tokens = useAppDesignTokens();
24
+ const { top, bottom } = useSafeAreaInsets();
25
+ const { t } = useLocalization();
26
+ const navigation = useAppNavigation();
27
+ const route = useAppRoute<{ params: RouteParams }, "params">();
28
+
29
+ const categoryId = route.params?.categoryId ?? "romantic";
30
+ const messages = useMemo(() => CATEGORY_TEMPLATES[categoryId] || [], [categoryId]);
31
+
32
+ const categoryTitle = useMemo(() => {
33
+ if (categoryId === "trending") return t("loveMessage.explore.trending");
34
+ const config = MESSAGE_TYPES.find((c) => c.type === categoryId);
35
+ return config ? t(config.labelKey) : categoryId;
36
+ }, [categoryId, t]);
37
+
38
+ const handleBack = () => navigation.goBack();
39
+
40
+ const handleNavigateToGenerator = (type?: string) => {
41
+ navigation.navigate("MessageGenerator", { initialType: type as MessageType });
42
+ };
43
+
44
+ const handleShare = useCallback(async (text: string) => {
45
+ try {
46
+ await Share.share({ message: text });
47
+ } catch {
48
+ /* Ignore */
49
+ }
50
+ }, []);
51
+
52
+ return (
53
+ <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
54
+ <View style={[styles.header, { paddingTop: top + tokens.spacing.md }]}>
55
+ <Pressable onPress={handleBack} style={styles.backBtn}>
56
+ <AtomicIcon name="arrow-back" color="textPrimary" size="sm" />
57
+ </Pressable>
58
+ <AtomicText type="headlineSmall" color="textPrimary" style={styles.headerTitle}>
59
+ {categoryTitle}
60
+ </AtomicText>
61
+ <View style={styles.spacer} />
62
+ </View>
63
+
64
+ <ScrollView
65
+ showsVerticalScrollIndicator={false}
66
+ contentContainerStyle={{ paddingBottom: bottom + 100, paddingHorizontal: 16 }}
67
+ >
68
+ <View style={styles.headlineContainer}>
69
+ <AtomicText type="headlineMedium" color="textPrimary" style={styles.headline}>
70
+ {`${messages.length} ${t("loveMessage.messages")}`}
71
+ </AtomicText>
72
+ <AtomicText type="bodySmall" color="textTertiary">
73
+ {t("loveMessage.aiCrafted")}
74
+ </AtomicText>
75
+ </View>
76
+
77
+ {messages.map((item) => (
78
+ <MessageListItem key={item.id} item={item} onShare={handleShare} />
79
+ ))}
80
+ </ScrollView>
81
+
82
+ <View style={[styles.fabContainer, { bottom: bottom + tokens.spacing.lg }]}>
83
+ <Pressable
84
+ onPress={() => handleNavigateToGenerator(categoryId !== "trending" ? categoryId : undefined)}
85
+ style={[styles.fab, { backgroundColor: tokens.colors.primary }]}
86
+ >
87
+ <AtomicIcon name="sparkles" color="onPrimary" size="sm" />
88
+ <AtomicText type="labelLarge" color="onPrimary" style={styles.fabText}>
89
+ {t("loveMessage.generateMore")}
90
+ </AtomicText>
91
+ </Pressable>
92
+ </View>
93
+ </View>
94
+ );
95
+ };
96
+
97
+ const styles = StyleSheet.create({
98
+ container: { flex: 1 },
99
+ header: {
100
+ flexDirection: "row",
101
+ justifyContent: "space-between",
102
+ alignItems: "center",
103
+ paddingHorizontal: 8,
104
+ paddingBottom: 16,
105
+ },
106
+ backBtn: { padding: 12 },
107
+ headerTitle: { fontWeight: "bold" },
108
+ spacer: { width: 44 },
109
+ headlineContainer: { paddingVertical: 24 },
110
+ headline: { fontWeight: "bold", marginBottom: 4 },
111
+ fabContainer: {
112
+ position: "absolute",
113
+ left: 20,
114
+ right: 20,
115
+ alignItems: "center",
116
+ },
117
+ fab: {
118
+ flexDirection: "row",
119
+ alignItems: "center",
120
+ justifyContent: "center",
121
+ paddingVertical: 16,
122
+ paddingHorizontal: 24,
123
+ borderRadius: 32,
124
+ gap: 12,
125
+ },
126
+ fabText: { fontWeight: "bold", letterSpacing: 1 },
127
+ });
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Partner Profile Screen
3
+ */
4
+
5
+ import { FC } from "react";
6
+ import { View, ScrollView, StyleSheet } from "react-native";
7
+ import {
8
+ AtomicText,
9
+ AtomicButton,
10
+ useAppDesignTokens,
11
+ useSafeAreaInsets,
12
+ useAppNavigation,
13
+ } from "@umituz/react-native-design-system";
14
+ import { useLocalization } from "@umituz/react-native-localization";
15
+ import { LOVE_LANGUAGES } from "../../domain/constants";
16
+ import { usePartnerProfile } from "../hooks/usePartnerProfile";
17
+ import { FieldInput } from "../components/FieldInput";
18
+
19
+ export const PartnerProfileScreen: FC = () => {
20
+ const tokens = useAppDesignTokens();
21
+ const { top, bottom } = useSafeAreaInsets();
22
+ const { t } = useLocalization();
23
+ const navigation = useAppNavigation();
24
+ const p = usePartnerProfile(() => navigation.goBack());
25
+
26
+ if (p.isLoading) return null;
27
+
28
+ return (
29
+ <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
30
+ <View style={[styles.header, { paddingTop: top + tokens.spacing.md }]}>
31
+ <AtomicButton icon="arrow-back" onPress={() => navigation.goBack()} variant="text" size="sm" />
32
+ <View style={styles.headerTitle}>
33
+ <AtomicText type="headlineSmall" color="textPrimary" style={styles.headerText}>
34
+ {t("loveMessage.partnerProfile.title")}
35
+ </AtomicText>
36
+ </View>
37
+ </View>
38
+
39
+ <ScrollView
40
+ contentContainerStyle={{
41
+ paddingBottom: bottom + tokens.spacing.xl,
42
+ paddingHorizontal: tokens.spacing.lg,
43
+ }}
44
+ showsVerticalScrollIndicator={false}
45
+ >
46
+ <AtomicText type="bodyMedium" color="textSecondary" style={styles.subtitle}>
47
+ {t("loveMessage.partnerProfile.subtitle")}
48
+ </AtomicText>
49
+
50
+ <FieldInput
51
+ label={t("loveMessage.partnerName")}
52
+ value={p.profile.name}
53
+ onChange={(text) => p.setProfile((prev) => ({ ...prev, name: text }))}
54
+ placeholder={t("loveMessage.partnerNamePlaceholder")}
55
+ />
56
+
57
+ <View style={styles.field}>
58
+ <AtomicText type="labelLarge" color="textPrimary" style={styles.label}>
59
+ {t("loveMessage.partnerProfile.loveLanguage")}
60
+ </AtomicText>
61
+ <View style={styles.chipContainer}>
62
+ {LOVE_LANGUAGES.map((lang) => (
63
+ <AtomicButton
64
+ key={lang.language}
65
+ title={t(lang.labelKey)}
66
+ onPress={() => p.setProfile((prev) => ({ ...prev, loveLanguage: lang.language }))}
67
+ variant={p.profile.loveLanguage === lang.language ? "primary" : "outline"}
68
+ size="sm"
69
+ style={styles.chip}
70
+ />
71
+ ))}
72
+ </View>
73
+ </View>
74
+
75
+ <FieldInput
76
+ label={t("loveMessage.partnerProfile.traits")}
77
+ value={p.profile.traits}
78
+ onChange={(text) => p.setProfile((prev) => ({ ...prev, traits: text }))}
79
+ placeholder={t("loveMessage.partnerProfile.traitsPlaceholder")}
80
+ />
81
+
82
+ <FieldInput
83
+ label={t("loveMessage.partnerProfile.quirks")}
84
+ value={p.profile.quirks}
85
+ onChange={(text) => p.setProfile((prev) => ({ ...prev, quirks: text }))}
86
+ placeholder={t("loveMessage.partnerProfile.quirksPlaceholder")}
87
+ multiline
88
+ />
89
+
90
+ <AtomicButton
91
+ title={t("loveMessage.partnerProfile.save")}
92
+ onPress={p.handleSave}
93
+ disabled={!p.profile.name.trim()}
94
+ variant="primary"
95
+ fullWidth
96
+ style={styles.saveBtn}
97
+ />
98
+ </ScrollView>
99
+ </View>
100
+ );
101
+ };
102
+
103
+ const styles = StyleSheet.create({
104
+ container: { flex: 1 },
105
+ header: {
106
+ flexDirection: "row",
107
+ alignItems: "center",
108
+ paddingHorizontal: 8,
109
+ paddingBottom: 24,
110
+ },
111
+ headerTitle: { flex: 1, marginRight: 40, alignItems: "center" },
112
+ headerText: { fontWeight: "bold" },
113
+ subtitle: { marginBottom: 32 },
114
+ field: { marginBottom: 24 },
115
+ label: { marginBottom: 10, marginLeft: 4 },
116
+ chipContainer: { flexDirection: "row", flexWrap: "wrap", gap: 10 },
117
+ chip: { borderRadius: 20 },
118
+ saveBtn: { height: 56, borderRadius: 16, marginTop: 16 },
119
+ });
@@ -139,6 +139,55 @@ export interface ScenarioConfig {
139
139
  readonly title: string;
140
140
  }
141
141
 
142
+ /**
143
+ * Visual style option for prompt customization
144
+ */
145
+ export interface VisualStyleOption {
146
+ readonly id: string;
147
+ readonly icon: string;
148
+ readonly labelKey: string;
149
+ }
150
+
151
+ /**
152
+ * Inspiration chip data for prompt customization
153
+ */
154
+ export interface InspirationChipData {
155
+ readonly id: string;
156
+ readonly labelKey: string;
157
+ readonly promptKey: string;
158
+ }
159
+
160
+ /**
161
+ * Magic prompt configuration
162
+ */
163
+ export interface MagicPromptConfig {
164
+ readonly headerKey: string;
165
+ readonly headlinePart1Key: string;
166
+ readonly headlinePart2Key: string;
167
+ readonly subtitleKey: string;
168
+ readonly inputLabelKey: string;
169
+ readonly surpriseButtonKey: string;
170
+ readonly placeholderKey: string;
171
+ readonly styleTitleKey: string;
172
+ readonly inspirationTitleKey: string;
173
+ readonly continueKey: string;
174
+ readonly maxLength: number;
175
+ readonly minLength: number;
176
+ }
177
+
178
+ /**
179
+ * Couple feature identifier
180
+ */
181
+ export type CoupleFeatureId = string;
182
+
183
+ /**
184
+ * Couple feature selection state
185
+ */
186
+ export interface CoupleFeatureSelection {
187
+ readonly featureId: CoupleFeatureId | null;
188
+ readonly [key: string]: any;
189
+ }
190
+
142
191
  export interface ScenarioData {
143
192
  readonly id: string;
144
193
  readonly category?: string;
@@ -50,4 +50,9 @@ export type {
50
50
  ScenarioSelectorConfig,
51
51
  ScenarioPreviewTranslations,
52
52
  ScenarioConfig,
53
+ VisualStyleOption,
54
+ InspirationChipData,
55
+ MagicPromptConfig,
56
+ CoupleFeatureId,
57
+ CoupleFeatureSelection,
53
58
  } from "./domain/scenario.types";
package/src/index.ts CHANGED
@@ -141,6 +141,7 @@ export * from "./domains/content-moderation";
141
141
  export * from "./domains/creations";
142
142
  export * from "./domains/face-detection";
143
143
  export * from "./domains/scenarios";
144
+ export * from "./domains/love-message";
144
145
  export * from "./infrastructure/orchestration";
145
146
 
146
147
  // Generation Config Provider (App Configuration)