@umituz/react-native-ai-generation-content 1.17.251 → 1.17.254
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/features/couple-future/infrastructure/coupleFeatureRegistry.ts +1 -1
- package/src/features/love-message/domain/constants.ts +162 -0
- package/src/features/love-message/domain/types.ts +58 -0
- package/src/features/love-message/index.ts +35 -0
- package/src/features/love-message/infrastructure/persistence/PartnerProfileRepository.ts +58 -0
- package/src/features/love-message/infrastructure/prompts/messagePromptBuilder.ts +109 -0
- package/src/features/love-message/infrastructure/services/LoveMessageService.ts +35 -0
- package/src/features/love-message/presentation/components/CategoryGrid.tsx +87 -0
- package/src/features/love-message/presentation/components/DetailsInput.tsx +74 -0
- package/src/features/love-message/presentation/components/ExploreHeader.tsx +67 -0
- package/src/features/love-message/presentation/components/FieldInput.tsx +83 -0
- package/src/features/love-message/presentation/components/GeneratorHeader.tsx +90 -0
- package/src/features/love-message/presentation/components/LoveMessageHeroSection.tsx +114 -0
- package/src/features/love-message/presentation/components/MessageListItem.tsx +76 -0
- package/src/features/love-message/presentation/components/MessageResult.tsx +105 -0
- package/src/features/love-message/presentation/components/PartnerInput.tsx +78 -0
- package/src/features/love-message/presentation/components/ProgressDots.tsx +48 -0
- package/src/features/love-message/presentation/components/StepDetails.tsx +23 -0
- package/src/features/love-message/presentation/components/StepPartner.tsx +116 -0
- package/src/features/love-message/presentation/components/StepVibe.tsx +30 -0
- package/src/features/love-message/presentation/components/ToneSelector.tsx +99 -0
- package/src/features/love-message/presentation/components/TrendingSection.tsx +130 -0
- package/src/features/love-message/presentation/components/TypeSelector.tsx +98 -0
- package/src/features/love-message/presentation/hooks/useLoveMessageGenerator.ts +111 -0
- package/src/features/love-message/presentation/hooks/usePartnerProfile.ts +46 -0
- package/src/features/love-message/presentation/navigation/LoveMessageStack.tsx +40 -0
- package/src/features/love-message/presentation/screens/LoveMessageExploreScreen.tsx +89 -0
- package/src/features/love-message/presentation/screens/LoveMessageGeneratorScreen.tsx +128 -0
- package/src/features/love-message/presentation/screens/MessageListScreen.tsx +99 -0
- package/src/features/love-message/presentation/screens/PartnerProfileScreen.tsx +100 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Love Message Explore Screen
|
|
3
|
+
* Premium entry point for Love Message domain
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { FC, useCallback } from "react";
|
|
7
|
+
import { View, ScrollView, StyleSheet, Pressable } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
AtomicIcon,
|
|
11
|
+
useAppDesignTokens,
|
|
12
|
+
useSafeAreaInsets,
|
|
13
|
+
} from "@umituz/react-native-design-system";
|
|
14
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
15
|
+
import { useNavigation } from "@react-navigation/native";
|
|
16
|
+
import { ExploreHeader } from "../components/ExploreHeader";
|
|
17
|
+
import { HeroSection } from "../components/HeroSection";
|
|
18
|
+
import { CategoryGrid } from "../components/CategoryGrid";
|
|
19
|
+
import { TrendingSection } from "../components/TrendingSection";
|
|
20
|
+
|
|
21
|
+
interface LoveMessageExploreScreenProps {
|
|
22
|
+
onNavigateToGenerator: () => void;
|
|
23
|
+
onNavigateToCategory: (categoryId: string) => void;
|
|
24
|
+
onNavigateToTrending: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const LoveMessageExploreScreen: FC<LoveMessageExploreScreenProps> = ({
|
|
28
|
+
onNavigateToGenerator,
|
|
29
|
+
onNavigateToCategory,
|
|
30
|
+
onNavigateToTrending,
|
|
31
|
+
}) => {
|
|
32
|
+
const tokens = useAppDesignTokens();
|
|
33
|
+
const { bottom } = useSafeAreaInsets();
|
|
34
|
+
const { t } = useLocalization();
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
38
|
+
<ExploreHeader onMagicPress={onNavigateToGenerator} />
|
|
39
|
+
|
|
40
|
+
<ScrollView
|
|
41
|
+
showsVerticalScrollIndicator={false}
|
|
42
|
+
contentContainerStyle={{ paddingBottom: bottom + 100 }}
|
|
43
|
+
>
|
|
44
|
+
<HeroSection />
|
|
45
|
+
|
|
46
|
+
<CategoryGrid
|
|
47
|
+
onCategoryPress={onNavigateToCategory}
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<TrendingSection
|
|
51
|
+
onViewAll={onNavigateToTrending}
|
|
52
|
+
/>
|
|
53
|
+
</ScrollView>
|
|
54
|
+
|
|
55
|
+
{/* Floating Action Button */}
|
|
56
|
+
<View style={[styles.fabContainer, { bottom: bottom + tokens.spacing.lg }]}>
|
|
57
|
+
<Pressable
|
|
58
|
+
onPress={onNavigateToGenerator}
|
|
59
|
+
style={[styles.fab, { backgroundColor: tokens.colors.primary }]}
|
|
60
|
+
>
|
|
61
|
+
<AtomicIcon name="sparkles" color="onPrimary" size="sm" />
|
|
62
|
+
<AtomicText type="labelLarge" color="onPrimary" style={styles.fabText}>
|
|
63
|
+
{t("loveMessage.explore.generateMagic")}
|
|
64
|
+
</AtomicText>
|
|
65
|
+
</Pressable>
|
|
66
|
+
</View>
|
|
67
|
+
</View>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const styles = StyleSheet.create({
|
|
72
|
+
container: { flex: 1 },
|
|
73
|
+
fabContainer: {
|
|
74
|
+
position: 'absolute',
|
|
75
|
+
left: 20,
|
|
76
|
+
right: 20,
|
|
77
|
+
alignItems: 'center',
|
|
78
|
+
},
|
|
79
|
+
fab: {
|
|
80
|
+
flexDirection: 'row',
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
justifyContent: 'center',
|
|
83
|
+
paddingVertical: 18,
|
|
84
|
+
paddingHorizontal: 32,
|
|
85
|
+
borderRadius: 32,
|
|
86
|
+
gap: 12,
|
|
87
|
+
},
|
|
88
|
+
fabText: { fontWeight: 'bold', letterSpacing: 1 },
|
|
89
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Love Message Generator Screen
|
|
3
|
+
* Optimized multi-step wizard flow
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { FC, useMemo } from "react";
|
|
7
|
+
import { View, ScrollView, StyleSheet, Animated } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
AtomicButton,
|
|
11
|
+
useAppDesignTokens,
|
|
12
|
+
useSafeAreaInsets,
|
|
13
|
+
} from "@umituz/react-native-design-system";
|
|
14
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
15
|
+
import { useNavigation } from "@react-navigation/native";
|
|
16
|
+
import { ProgressDots } from "../components/ProgressDots";
|
|
17
|
+
import { MessageResult } from "../components/MessageResult";
|
|
18
|
+
import { GeneratorHeader } from "../components/GeneratorHeader";
|
|
19
|
+
import { StepPartner } from "../components/StepPartner";
|
|
20
|
+
import { StepVibe } from "../components/StepVibe";
|
|
21
|
+
import { StepDetails } from "../components/StepDetails";
|
|
22
|
+
import { useLoveMessageGenerator, GeneratorStep } from "../hooks/useLoveMessageGenerator";
|
|
23
|
+
|
|
24
|
+
interface LoveMessageGeneratorScreenProps {
|
|
25
|
+
onBack: () => void;
|
|
26
|
+
onNavigateToProfile: () => void;
|
|
27
|
+
initialType?: MessageType;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const LoveMessageGeneratorScreen: FC<LoveMessageGeneratorScreenProps> = ({
|
|
31
|
+
onBack,
|
|
32
|
+
onNavigateToProfile,
|
|
33
|
+
initialType,
|
|
34
|
+
}) => {
|
|
35
|
+
const tokens = useAppDesignTokens();
|
|
36
|
+
const { bottom } = useSafeAreaInsets();
|
|
37
|
+
const { t } = useLocalization();
|
|
38
|
+
const gen = useLoveMessageGenerator({ onBack, initialType });
|
|
39
|
+
|
|
40
|
+
const stepTitle = useMemo(() => {
|
|
41
|
+
switch (gen.currentStep) {
|
|
42
|
+
case GeneratorStep.PARTNER: return t("loveMessage.generator.stepPartner");
|
|
43
|
+
case GeneratorStep.VIBE: return t("loveMessage.generator.stepVibe");
|
|
44
|
+
case GeneratorStep.DETAILS: return t("loveMessage.generator.stepDetails");
|
|
45
|
+
case GeneratorStep.RESULT: return t("loveMessage.generator.stepResult");
|
|
46
|
+
default: return "";
|
|
47
|
+
}
|
|
48
|
+
}, [gen.currentStep, t]);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
52
|
+
<GeneratorHeader
|
|
53
|
+
currentStep={gen.currentStep}
|
|
54
|
+
partnerName={gen.partnerName}
|
|
55
|
+
onBack={gen.handleBack}
|
|
56
|
+
onNext={gen.handleNext}
|
|
57
|
+
onGenerate={gen.handleGenerate}
|
|
58
|
+
isGenerating={gen.isGenerating}
|
|
59
|
+
/>
|
|
60
|
+
|
|
61
|
+
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ paddingBottom: bottom + 120 }}>
|
|
62
|
+
<View style={styles.heroSection}>
|
|
63
|
+
<ProgressDots currentStep={gen.currentStep} totalSteps={4} />
|
|
64
|
+
<AtomicText type="headlineLarge" color="textPrimary" style={styles.heroTitle}>
|
|
65
|
+
{stepTitle}
|
|
66
|
+
</AtomicText>
|
|
67
|
+
</View>
|
|
68
|
+
|
|
69
|
+
<View style={styles.formContent}>
|
|
70
|
+
{gen.currentStep === GeneratorStep.PARTNER && (
|
|
71
|
+
<Animated.View>
|
|
72
|
+
<StepPartner
|
|
73
|
+
partnerName={gen.partnerName}
|
|
74
|
+
setPartnerName={gen.setPartnerName}
|
|
75
|
+
useProfile={gen.useProfile}
|
|
76
|
+
setUseProfile={gen.setUseProfile}
|
|
77
|
+
hasProfile={gen.hasProfile}
|
|
78
|
+
onEditProfile={onNavigateToProfile}
|
|
79
|
+
/>
|
|
80
|
+
</Animated.View>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{gen.currentStep === GeneratorStep.VIBE && (
|
|
84
|
+
<Animated.View>
|
|
85
|
+
<StepVibe
|
|
86
|
+
selectedType={gen.selectedType}
|
|
87
|
+
setSelectedType={gen.setSelectedType}
|
|
88
|
+
selectedTone={gen.selectedTone}
|
|
89
|
+
setSelectedTone={gen.setSelectedTone}
|
|
90
|
+
/>
|
|
91
|
+
</Animated.View>
|
|
92
|
+
)}
|
|
93
|
+
|
|
94
|
+
{gen.currentStep === GeneratorStep.DETAILS && (
|
|
95
|
+
<Animated.View>
|
|
96
|
+
<StepDetails details={gen.details} setDetails={gen.setDetails} />
|
|
97
|
+
</Animated.View>
|
|
98
|
+
)}
|
|
99
|
+
|
|
100
|
+
{gen.currentStep === GeneratorStep.RESULT && (
|
|
101
|
+
<MessageResult message={gen.generatedMessage} />
|
|
102
|
+
)}
|
|
103
|
+
</View>
|
|
104
|
+
</ScrollView>
|
|
105
|
+
|
|
106
|
+
{gen.currentStep === GeneratorStep.RESULT && (
|
|
107
|
+
<View style={[styles.footer, { paddingBottom: bottom + 24 }]}>
|
|
108
|
+
<AtomicButton
|
|
109
|
+
title={t("loveMessage.generator.startOver")}
|
|
110
|
+
onPress={gen.startOver}
|
|
111
|
+
variant="outline"
|
|
112
|
+
fullWidth
|
|
113
|
+
style={styles.actionBtn}
|
|
114
|
+
/>
|
|
115
|
+
</View>
|
|
116
|
+
)}
|
|
117
|
+
</View>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const styles = StyleSheet.create({
|
|
122
|
+
container: { flex: 1 },
|
|
123
|
+
heroSection: { paddingVertical: 32, alignItems: 'center' },
|
|
124
|
+
heroTitle: { fontWeight: 'bold', textAlign: 'center', marginBottom: 8, paddingHorizontal: 20 },
|
|
125
|
+
formContent: { paddingHorizontal: 20 },
|
|
126
|
+
footer: { position: 'absolute', bottom: 0, left: 0, right: 0, padding: 24, backgroundColor: 'transparent' },
|
|
127
|
+
actionBtn: { height: 60, borderRadius: 30 },
|
|
128
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
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
|
+
useAppDesignTokens,
|
|
11
|
+
useSafeAreaInsets,
|
|
12
|
+
} from "@umituz/react-native-design-system";
|
|
13
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
14
|
+
import { useNavigation, useRoute } from "@react-navigation/native";
|
|
15
|
+
import { CATEGORY_TEMPLATES, MESSAGE_TYPES } from "../../domain/constants";
|
|
16
|
+
import { MessageListItem } from "../components/MessageListItem";
|
|
17
|
+
|
|
18
|
+
interface MessageListScreenProps {
|
|
19
|
+
categoryId: string;
|
|
20
|
+
onBack: () => void;
|
|
21
|
+
onNavigateToGenerator: (initialType?: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const MessageListScreen: FC<MessageListScreenProps> = ({
|
|
25
|
+
categoryId,
|
|
26
|
+
onBack,
|
|
27
|
+
onNavigateToGenerator,
|
|
28
|
+
}) => {
|
|
29
|
+
const tokens = useAppDesignTokens();
|
|
30
|
+
const { top, bottom } = useSafeAreaInsets();
|
|
31
|
+
const { t } = useLocalization();
|
|
32
|
+
|
|
33
|
+
const messages = useMemo(() => CATEGORY_TEMPLATES[categoryId] || [], [categoryId]);
|
|
34
|
+
|
|
35
|
+
const categoryTitle = useMemo(() => {
|
|
36
|
+
if (categoryId === "trending") return t("loveMessage.explore.trending");
|
|
37
|
+
const config = MESSAGE_TYPES.find(c => c.type === categoryId);
|
|
38
|
+
return config ? t(config.labelKey) : categoryId;
|
|
39
|
+
}, [categoryId, t]);
|
|
40
|
+
|
|
41
|
+
const handleShare = useCallback(async (text: string) => {
|
|
42
|
+
try {
|
|
43
|
+
await Share.share({ message: text });
|
|
44
|
+
} catch { /* Ignore */ }
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
49
|
+
<View style={[styles.header, { paddingTop: top + tokens.spacing.md }]}>
|
|
50
|
+
<Pressable onPress={onBack} style={styles.backBtn}>
|
|
51
|
+
<AtomicIcon name="arrow-back" color="textPrimary" size="sm" />
|
|
52
|
+
</Pressable>
|
|
53
|
+
<AtomicText type="headlineSmall" color="textPrimary" style={styles.headerTitle}>
|
|
54
|
+
{categoryTitle}
|
|
55
|
+
</AtomicText>
|
|
56
|
+
<View style={{ width: 44 }} />
|
|
57
|
+
</View>
|
|
58
|
+
|
|
59
|
+
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ paddingBottom: bottom + 100, paddingHorizontal: 16 }}>
|
|
60
|
+
<View style={styles.headlineContainer}>
|
|
61
|
+
<AtomicText type="headlineMedium" color="textPrimary" style={styles.headline}>
|
|
62
|
+
{`${messages.length} messages`}
|
|
63
|
+
</AtomicText>
|
|
64
|
+
<AtomicText type="bodySmall" color="textTertiary">
|
|
65
|
+
AI-crafted for your special moments
|
|
66
|
+
</AtomicText>
|
|
67
|
+
</View>
|
|
68
|
+
|
|
69
|
+
{messages.map((item) => (
|
|
70
|
+
<MessageListItem key={item.id} item={item} onShare={handleShare} />
|
|
71
|
+
))}
|
|
72
|
+
</ScrollView>
|
|
73
|
+
|
|
74
|
+
<View style={[styles.fabContainer, { bottom: bottom + tokens.spacing.lg }]}>
|
|
75
|
+
<Pressable
|
|
76
|
+
onPress={() => onNavigateToGenerator(categoryId !== "trending" ? categoryId : undefined)}
|
|
77
|
+
style={[styles.fab, { backgroundColor: tokens.colors.primary }]}
|
|
78
|
+
>
|
|
79
|
+
<AtomicIcon name="sparkles" color="onPrimary" size="sm" />
|
|
80
|
+
<AtomicText type="labelLarge" color="onPrimary" style={styles.fabText}>
|
|
81
|
+
GENERATE MORE
|
|
82
|
+
</AtomicText>
|
|
83
|
+
</Pressable>
|
|
84
|
+
</View>
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const styles = StyleSheet.create({
|
|
90
|
+
container: { flex: 1 },
|
|
91
|
+
header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 8, paddingBottom: 16 },
|
|
92
|
+
backBtn: { padding: 12 },
|
|
93
|
+
headerTitle: { fontWeight: 'bold' },
|
|
94
|
+
headlineContainer: { paddingVertical: 24 },
|
|
95
|
+
headline: { fontWeight: 'bold', marginBottom: 4 },
|
|
96
|
+
fabContainer: { position: "absolute", left: 20, right: 20, alignItems: "center" },
|
|
97
|
+
fab: { flexDirection: "row", alignItems: "center", justifyContent: "center", paddingVertical: 16, paddingHorizontal: 24, borderRadius: 32, gap: 12 },
|
|
98
|
+
fabText: { fontWeight: "bold", letterSpacing: 1 },
|
|
99
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Partner Profile Screen
|
|
3
|
+
* Optimized version with hooks and components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { FC } from "react";
|
|
7
|
+
import { View, ScrollView, StyleSheet } from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
AtomicText,
|
|
10
|
+
AtomicButton,
|
|
11
|
+
useAppDesignTokens,
|
|
12
|
+
useSafeAreaInsets,
|
|
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 p = usePartnerProfile();
|
|
24
|
+
|
|
25
|
+
if (p.isLoading) return null;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
29
|
+
<View style={[styles.header, { paddingTop: top + tokens.spacing.md }]}>
|
|
30
|
+
<AtomicButton icon="arrow-back" onPress={() => p.navigation.goBack()} variant="text" size="sm" />
|
|
31
|
+
<View style={styles.headerTitle}>
|
|
32
|
+
<AtomicText type="headlineSmall" color="textPrimary" style={styles.headerText}>
|
|
33
|
+
{t("loveMessage.partnerProfile.title")}
|
|
34
|
+
</AtomicText>
|
|
35
|
+
</View>
|
|
36
|
+
</View>
|
|
37
|
+
|
|
38
|
+
<ScrollView
|
|
39
|
+
contentContainerStyle={{ paddingBottom: bottom + tokens.spacing.xl, paddingHorizontal: tokens.spacing.lg }}
|
|
40
|
+
showsVerticalScrollIndicator={false}
|
|
41
|
+
>
|
|
42
|
+
<AtomicText type="bodyMedium" color="textSecondary" style={styles.subtitle}>
|
|
43
|
+
{t("loveMessage.partnerProfile.subtitle")}
|
|
44
|
+
</AtomicText>
|
|
45
|
+
|
|
46
|
+
<FieldInput
|
|
47
|
+
label={t("loveMessage.partnerName")} value={p.profile.name}
|
|
48
|
+
onChange={(text) => p.setProfile((prev) => ({ ...prev, name: text }))}
|
|
49
|
+
placeholder={t("loveMessage.partnerNamePlaceholder")}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<View style={styles.field}>
|
|
53
|
+
<AtomicText type="labelLarge" color="textPrimary" style={styles.label}>
|
|
54
|
+
{t("loveMessage.partnerProfile.loveLanguage")}
|
|
55
|
+
</AtomicText>
|
|
56
|
+
<View style={styles.chipContainer}>
|
|
57
|
+
{LOVE_LANGUAGES.map((lang) => (
|
|
58
|
+
<AtomicButton
|
|
59
|
+
key={lang.language} title={t(lang.labelKey)}
|
|
60
|
+
onPress={() => p.setProfile((prev) => ({ ...prev, loveLanguage: lang.language }))}
|
|
61
|
+
variant={p.profile.loveLanguage === lang.language ? "primary" : "outline"}
|
|
62
|
+
size="sm" style={styles.chip}
|
|
63
|
+
/>
|
|
64
|
+
))}
|
|
65
|
+
</View>
|
|
66
|
+
</View>
|
|
67
|
+
|
|
68
|
+
<FieldInput
|
|
69
|
+
label={t("loveMessage.partnerProfile.traits")} value={p.profile.traits}
|
|
70
|
+
onChange={(text) => p.setProfile((prev) => ({ ...prev, traits: text }))}
|
|
71
|
+
placeholder={t("loveMessage.partnerProfile.traitsPlaceholder")}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
<FieldInput
|
|
75
|
+
label={t("loveMessage.partnerProfile.quirks")} value={p.profile.quirks}
|
|
76
|
+
onChange={(text) => p.setProfile((prev) => ({ ...prev, quirks: text }))}
|
|
77
|
+
placeholder={t("loveMessage.partnerProfile.quirksPlaceholder")} multiline
|
|
78
|
+
/>
|
|
79
|
+
|
|
80
|
+
<AtomicButton
|
|
81
|
+
title={t("loveMessage.partnerProfile.save")} onPress={p.handleSave}
|
|
82
|
+
disabled={!p.profile.name.trim()} variant="primary" fullWidth style={styles.saveBtn}
|
|
83
|
+
/>
|
|
84
|
+
</ScrollView>
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const styles = StyleSheet.create({
|
|
90
|
+
container: { flex: 1 },
|
|
91
|
+
header: { flexDirection: "row", alignItems: "center", paddingHorizontal: 8, paddingBottom: 24 },
|
|
92
|
+
headerTitle: { flex: 1, marginRight: 40, alignItems: "center" },
|
|
93
|
+
headerText: { fontWeight: "bold" },
|
|
94
|
+
subtitle: { marginBottom: 32 },
|
|
95
|
+
field: { marginBottom: 24 },
|
|
96
|
+
label: { marginBottom: 10, marginLeft: 4 },
|
|
97
|
+
chipContainer: { flexDirection: "row", flexWrap: "wrap", gap: 10 },
|
|
98
|
+
chip: { borderRadius: 20 },
|
|
99
|
+
saveBtn: { height: 56, borderRadius: 16, marginTop: 16 },
|
|
100
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -151,6 +151,7 @@ export * from "./features/text-to-voice";
|
|
|
151
151
|
export * from "./features/hd-touch-up";
|
|
152
152
|
export * from "./features/meme-generator";
|
|
153
153
|
export * from "./features/couple-future";
|
|
154
|
+
export * from "./features/love-message";
|
|
154
155
|
export * from "./infrastructure/orchestration";
|
|
155
156
|
|
|
156
157
|
// Result Preview Domain
|