@umituz/react-native-ai-generation-content 1.17.257 → 1.17.258

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.17.257",
3
+ "version": "1.17.258",
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",
@@ -62,7 +62,7 @@
62
62
  "@types/react": "~19.1.10",
63
63
  "@typescript-eslint/eslint-plugin": "^8.0.0",
64
64
  "@typescript-eslint/parser": "^8.0.0",
65
- "@umituz/react-native-design-system": "^2.6.126",
65
+ "@umituz/react-native-design-system": "^2.8.5",
66
66
  "@umituz/react-native-filesystem": "latest",
67
67
  "@umituz/react-native-firebase": "latest",
68
68
  "@umituz/react-native-haptics": "latest",
@@ -84,14 +84,14 @@ export class CreationsWriter {
84
84
  if (updates.output !== undefined) {
85
85
  updateData.output = updates.output;
86
86
  }
87
- if ((updates as any).rating !== undefined) {
88
- updateData.rating = (updates as any).rating;
87
+ if (updates.rating !== undefined) {
88
+ updateData.rating = updates.rating;
89
89
  }
90
- if ((updates as any).ratedAt !== undefined) {
91
- updateData.ratedAt = (updates as any).ratedAt;
90
+ if (updates.ratedAt !== undefined) {
91
+ updateData.ratedAt = updates.ratedAt;
92
92
  }
93
- if ((updates as any).isFavorite !== undefined) {
94
- updateData.isFavorite = (updates as any).isFavorite;
93
+ if (updates.isFavorite !== undefined) {
94
+ updateData.isFavorite = updates.isFavorite;
95
95
  }
96
96
 
97
97
  await updateDoc(docRef, updateData);
@@ -30,7 +30,6 @@ export { GalleryHeader } from "./GalleryHeader";
30
30
  export { EmptyState } from "./EmptyState";
31
31
  export { GalleryEmptyStates } from "./GalleryEmptyStates";
32
32
  export { CreationsHomeCard } from "./CreationsHomeCard";
33
- export { CreationImageViewer } from "./CreationImageViewer";
34
33
  export { CreationsGrid } from "./CreationsGrid";
35
34
 
36
35
  // Detail Components
@@ -1,7 +1,7 @@
1
- import React, { useMemo, useState, useCallback } from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import { View, ScrollView, StyleSheet } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
- import { useAppDesignTokens, ImageGallery } from "@umituz/react-native-design-system";
4
+ import { useAppDesignTokens } from "@umituz/react-native-design-system";
5
5
  import type { Creation } from '../../domain/entities/Creation';
6
6
  import type { CreationsConfig } from '../../domain/value-objects/CreationsConfig';
7
7
  import { hasVideoContent, getPreviewUrl } from '../../domain/utils';
@@ -44,15 +44,6 @@ export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
44
44
  }) => {
45
45
  const tokens = useAppDesignTokens();
46
46
  const insets = useSafeAreaInsets();
47
- const [showFullScreen, setShowFullScreen] = useState(false);
48
-
49
- const handleImagePress = useCallback(() => {
50
- setShowFullScreen(true);
51
- }, []);
52
-
53
- const handleDismissFullScreen = useCallback(() => {
54
- setShowFullScreen(false);
55
- }, []);
56
47
 
57
48
  // Extract data safely
58
49
  const metadata = (creation.metadata || {}) as CreationMetadata;
@@ -89,7 +80,7 @@ export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
89
80
  {isVideo ? (
90
81
  <DetailVideo videoUrl={videoUrl} _thumbnailUrl={thumbnailUrl} />
91
82
  ) : (
92
- <DetailImage uri={creation.uri} onPress={handleImagePress} />
83
+ <DetailImage uri={creation.uri} />
93
84
  )}
94
85
 
95
86
  <DetailInfo title={title} date={date} />
@@ -107,15 +98,6 @@ export const CreationDetailScreen: React.FC<CreationDetailScreenProps> = ({
107
98
  viewResultLabel={onViewResult ? t("result.viewResult") : undefined}
108
99
  />
109
100
  </ScrollView>
110
-
111
- {!isVideo && (
112
- <ImageGallery
113
- images={[{ uri: creation.uri }]}
114
- visible={showFullScreen}
115
- onDismiss={handleDismissFullScreen}
116
- index={0}
117
- />
118
- )}
119
101
  </View>
120
102
  );
121
103
  };
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useCallback, useState } from "react";
1
+ import React, { useState, useMemo, useCallback } from "react";
2
2
  import { View, FlatList, RefreshControl, StyleSheet } from "react-native";
3
3
  import { useSafeAreaInsets } from "react-native-safe-area-context";
4
4
  import {
@@ -13,7 +13,7 @@ import { useFocusEffect } from "@react-navigation/native";
13
13
  import { useCreations } from "../hooks/useCreations";
14
14
  import { useDeleteCreation } from "../hooks/useDeleteCreation";
15
15
  import { useGalleryFilters } from "../hooks/useGalleryFilters";
16
- import { GalleryHeader, CreationCard, CreationImageViewer, GalleryEmptyStates } from "../components";
16
+ import { GalleryHeader, CreationCard, GalleryEmptyStates } from "../components";
17
17
  import { MEDIA_FILTER_OPTIONS, STATUS_FILTER_OPTIONS } from "../../domain/types/creation-filter";
18
18
  import type { Creation } from "../../domain/entities/Creation";
19
19
  import type { CreationsConfig } from "../../domain/value-objects/CreationsConfig";
@@ -26,8 +26,6 @@ interface CreationsGalleryScreenProps {
26
26
  readonly config: CreationsConfig;
27
27
  readonly t: (key: string) => string;
28
28
  readonly locale?: string;
29
- readonly enableEditing?: boolean;
30
- readonly onImageEdit?: (uri: string, creationId: string) => void | Promise<void>;
31
29
  readonly onEmptyAction?: () => void;
32
30
  readonly emptyActionLabel?: string;
33
31
  readonly showFilter?: boolean;
@@ -39,8 +37,6 @@ export function CreationsGalleryScreen({
39
37
  repository,
40
38
  config,
41
39
  t,
42
- enableEditing = false,
43
- onImageEdit,
44
40
  onEmptyAction,
45
41
  emptyActionLabel,
46
42
  showFilter = config.showFilter ?? true,
@@ -51,8 +47,6 @@ export function CreationsGalleryScreen({
51
47
  const { share } = useSharing();
52
48
  const alert = useAlert();
53
49
 
54
- const [viewerVisible, setViewerVisible] = useState(false);
55
- const [viewerIndex, setViewerIndex] = useState(0);
56
50
  const [selectedCreation, setSelectedCreation] = useState<Creation | null>(null);
57
51
 
58
52
  const { data: creations, isLoading, refetch } = useCreations({ userId, repository });
@@ -172,7 +166,6 @@ export function CreationsGalleryScreen({
172
166
  showsVerticalScrollIndicator={false}
173
167
  refreshControl={<RefreshControl refreshing={isLoading} onRefresh={() => void refetch()} tintColor={tokens.colors.primary} />}
174
168
  />
175
- <CreationImageViewer creations={filters.filtered} visible={viewerVisible} index={viewerIndex} onDismiss={() => setViewerVisible(false)} onIndexChange={setViewerIndex} enableEditing={enableEditing} onImageEdit={onImageEdit} />
176
169
  <FilterSheet visible={filters.statusFilterVisible} onClose={filters.closeStatusFilter} options={filters.statusFilter.filterOptions} selectedIds={[filters.statusFilter.selectedId]} onFilterPress={filters.statusFilter.selectFilter} onClearFilters={filters.statusFilter.clearFilter} title={t(config.translations.statusFilterTitle ?? "creations.filter.status")} clearLabel={t(config.translations.clearFilter ?? "common.clear")} />
177
170
  <FilterSheet visible={filters.mediaFilterVisible} onClose={filters.closeMediaFilter} options={filters.mediaFilter.filterOptions} selectedIds={[filters.mediaFilter.selectedId]} onFilterPress={filters.mediaFilter.selectFilter} onClearFilters={filters.mediaFilter.clearFilter} title={t(config.translations.mediaFilterTitle ?? "creations.filter.media")} clearLabel={t(config.translations.clearFilter ?? "common.clear")} />
178
171
  </View>
@@ -8,6 +8,7 @@ import {
8
8
  AtomicText,
9
9
  AtomicIcon,
10
10
  useAppDesignTokens,
11
+ type IconName,
11
12
  } from "@umituz/react-native-design-system";
12
13
  import { useLocalization } from "@umituz/react-native-localization";
13
14
  import { MESSAGE_TYPES } from "../../domain/constants";
@@ -41,7 +42,7 @@ export const CategoryGrid: React.FC<CategoryGridProps> = ({ onCategoryPress }) =
41
42
  }]}
42
43
  >
43
44
  <View style={[styles.categoryIconContainer, { backgroundColor: `${tokens.colors.primary}15` }]}>
44
- <AtomicIcon name={cat.icon as any} color="primary" size="md" />
45
+ <AtomicIcon name={cat.icon as IconName} color="primary" size="md" />
45
46
  </View>
46
47
  <AtomicText type="labelLarge" color="textPrimary">
47
48
  {t(cat.labelKey)}
@@ -11,9 +11,10 @@ import {
11
11
  useAppDesignTokens,
12
12
  } from "@umituz/react-native-design-system";
13
13
  import { useLocalization } from "@umituz/react-native-localization";
14
+ import type { TemplateMessage } from "../../domain/constants";
14
15
 
15
16
  interface MessageListItemProps {
16
- item: any;
17
+ item: TemplateMessage;
17
18
  onShare: (text: string) => void;
18
19
  }
19
20
 
@@ -9,6 +9,7 @@ import {
9
9
  AtomicText,
10
10
  AtomicIcon,
11
11
  useAppDesignTokens,
12
+ type IconName,
12
13
  } from "@umituz/react-native-design-system";
13
14
  import { useLocalization } from "@umituz/react-native-localization";
14
15
  import { MESSAGE_TONES } from "../../domain/constants";
@@ -55,7 +56,7 @@ export const ToneSelector: FC<ToneSelectorProps> = ({
55
56
  ]}
56
57
  >
57
58
  <AtomicIcon
58
- name={config.icon as any}
59
+ name={config.icon as IconName}
59
60
  color={isSelected ? "onPrimary" : "secondary"}
60
61
  size="sm"
61
62
  style={styles.icon}
@@ -9,6 +9,7 @@ import {
9
9
  AtomicText,
10
10
  AtomicIcon,
11
11
  useAppDesignTokens,
12
+ type IconName,
12
13
  } from "@umituz/react-native-design-system";
13
14
  import { useLocalization } from "@umituz/react-native-localization";
14
15
  import { MESSAGE_TYPES } from "../../domain/constants";
@@ -51,7 +52,7 @@ export const TypeSelector: FC<TypeSelectorProps> = ({
51
52
  ]}
52
53
  >
53
54
  <AtomicIcon
54
- name={config.icon as any}
55
+ name={config.icon as IconName}
55
56
  color={isSelected ? "onPrimary" : "secondary"}
56
57
  size="md"
57
58
  style={styles.icon}
@@ -9,11 +9,12 @@ import { LoveMessageExploreScreen } from "../screens/LoveMessageExploreScreen";
9
9
  import { MessageListScreen } from "../screens/MessageListScreen";
10
10
  import { LoveMessageGeneratorScreen } from "../screens/LoveMessageGeneratorScreen";
11
11
  import { PartnerProfileScreen } from "../screens/PartnerProfileScreen";
12
+ import type { MessageType } from "../../domain/types";
12
13
 
13
14
  export type LoveMessageStackParamList = {
14
15
  LoveMessageExplore: undefined;
15
- MessageList: { categoryId: string } | undefined;
16
- MessageGenerator: { initialType?: any } | undefined;
16
+ MessageList: { categoryId: string };
17
+ MessageGenerator: { initialType?: MessageType };
17
18
  PartnerProfile: undefined;
18
19
  };
19
20
 
@@ -21,19 +22,10 @@ const Stack = createStackNavigator<LoveMessageStackParamList>();
21
22
 
22
23
  export const LoveMessageStack: React.FC = () => {
23
24
  return (
24
- <Stack.Navigator
25
- screenOptions={{ headerShown: false }}
26
- initialRouteName="LoveMessageExplore"
27
- >
28
- <Stack.Screen
29
- name="LoveMessageExplore"
30
- component={LoveMessageExploreScreen}
31
- />
25
+ <Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName="LoveMessageExplore">
26
+ <Stack.Screen name="LoveMessageExplore" component={LoveMessageExploreScreen} />
32
27
  <Stack.Screen name="MessageList" component={MessageListScreen} />
33
- <Stack.Screen
34
- name="MessageGenerator"
35
- component={LoveMessageGeneratorScreen}
36
- />
28
+ <Stack.Screen name="MessageGenerator" component={LoveMessageGeneratorScreen} />
37
29
  <Stack.Screen name="PartnerProfile" component={PartnerProfileScreen} />
38
30
  </Stack.Navigator>
39
31
  );
@@ -8,43 +8,40 @@ import { View, ScrollView, StyleSheet } from "react-native";
8
8
  import {
9
9
  useAppDesignTokens,
10
10
  useSafeAreaInsets,
11
+ AppNavigation,
11
12
  } from "@umituz/react-native-design-system";
12
13
  import { ExploreHeader } from "../components/ExploreHeader";
13
14
  import { LoveMessageHeroSection } from "../components/LoveMessageHeroSection";
14
15
  import { CategoryGrid } from "../components/CategoryGrid";
15
16
  import { TrendingSection } from "../components/TrendingSection";
16
17
 
17
- interface LoveMessageExploreScreenProps {
18
- onNavigateToGenerator: () => void;
19
- onNavigateToCategory: (categoryId: string) => void;
20
- onNavigateToTrending: () => void;
21
- }
22
-
23
- export const LoveMessageExploreScreen: FC<LoveMessageExploreScreenProps> = ({
24
- onNavigateToGenerator,
25
- onNavigateToCategory,
26
- onNavigateToTrending,
27
- }) => {
18
+ export const LoveMessageExploreScreen: FC = () => {
28
19
  const tokens = useAppDesignTokens();
29
20
  const { bottom } = useSafeAreaInsets();
30
21
 
22
+ const handleNavigateToGenerator = () => {
23
+ AppNavigation.navigate("MessageGenerator", {});
24
+ };
25
+
26
+ const handleNavigateToCategory = (categoryId: string) => {
27
+ AppNavigation.navigate("MessageList", { categoryId });
28
+ };
29
+
30
+ const handleNavigateToTrending = () => {
31
+ AppNavigation.navigate("MessageList", { categoryId: "trending" });
32
+ };
33
+
31
34
  return (
32
35
  <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
33
- <ExploreHeader onMagicPress={onNavigateToGenerator} />
36
+ <ExploreHeader onMagicPress={handleNavigateToGenerator} />
34
37
 
35
- <ScrollView
38
+ <ScrollView
36
39
  showsVerticalScrollIndicator={false}
37
40
  contentContainerStyle={{ paddingBottom: bottom + 120 }}
38
41
  >
39
42
  <LoveMessageHeroSection />
40
-
41
- <CategoryGrid
42
- onCategoryPress={onNavigateToCategory}
43
- />
44
-
45
- <TrendingSection
46
- onViewAll={onNavigateToTrending}
47
- />
43
+ <CategoryGrid onCategoryPress={handleNavigateToCategory} />
44
+ <TrendingSection onViewAll={handleNavigateToTrending} />
48
45
  </ScrollView>
49
46
  </View>
50
47
  );
@@ -52,19 +49,4 @@ export const LoveMessageExploreScreen: FC<LoveMessageExploreScreenProps> = ({
52
49
 
53
50
  const styles = StyleSheet.create({
54
51
  container: { flex: 1 },
55
- fabContainer: {
56
- position: 'absolute',
57
- left: 0,
58
- right: 0,
59
- alignItems: 'center',
60
- zIndex: 10,
61
- },
62
- fab: {
63
- width: 64,
64
- height: 64,
65
- borderRadius: 32,
66
- alignItems: 'center',
67
- justifyContent: 'center',
68
- elevation: 8,
69
- },
70
52
  });
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Love Message Generator Screen
3
- * Optimized multi-step wizard flow
3
+ * Multi-step wizard flow
4
4
  */
5
5
 
6
6
  import { FC, useMemo } from "react";
@@ -10,8 +10,10 @@ import {
10
10
  AtomicButton,
11
11
  useAppDesignTokens,
12
12
  useSafeAreaInsets,
13
+ AppNavigation,
13
14
  } from "@umituz/react-native-design-system";
14
15
  import { useLocalization } from "@umituz/react-native-localization";
16
+ import { useRoute, RouteProp } from "@react-navigation/native";
15
17
  import { ProgressDots } from "../components/ProgressDots";
16
18
  import { MessageResult } from "../components/MessageResult";
17
19
  import { GeneratorHeader } from "../components/GeneratorHeader";
@@ -21,35 +23,37 @@ import { StepDetails } from "../components/StepDetails";
21
23
  import { MessageType } from "../../domain/types";
22
24
  import { useLoveMessageGenerator, GeneratorStep } from "../hooks/useLoveMessageGenerator";
23
25
 
24
- interface LoveMessageGeneratorScreenProps {
25
- onBack: () => void;
26
- onNavigateToProfile: () => void;
27
- initialType?: MessageType;
28
- }
26
+ type RouteParams = { initialType?: MessageType };
29
27
 
30
- export const LoveMessageGeneratorScreen: FC<LoveMessageGeneratorScreenProps> = ({
31
- onBack,
32
- onNavigateToProfile,
33
- initialType,
34
- }) => {
28
+ export const LoveMessageGeneratorScreen: FC = () => {
35
29
  const tokens = useAppDesignTokens();
36
30
  const { bottom } = useSafeAreaInsets();
37
31
  const { t } = useLocalization();
38
- const gen = useLoveMessageGenerator({ onBack, initialType });
32
+ const route = useRoute<RouteProp<{ params: RouteParams }, "params">>();
33
+
34
+ const initialType = route.params?.initialType;
35
+ const gen = useLoveMessageGenerator({ onBack: () => AppNavigation.goBack(), initialType });
36
+
37
+ const handleNavigateToProfile = () => AppNavigation.navigate("PartnerProfile");
39
38
 
40
39
  const stepTitle = useMemo(() => {
41
40
  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 "";
41
+ case GeneratorStep.PARTNER:
42
+ return t("loveMessage.generator.stepPartner");
43
+ case GeneratorStep.VIBE:
44
+ return t("loveMessage.generator.stepVibe");
45
+ case GeneratorStep.DETAILS:
46
+ return t("loveMessage.generator.stepDetails");
47
+ case GeneratorStep.RESULT:
48
+ return t("loveMessage.generator.stepResult");
49
+ default:
50
+ return "";
47
51
  }
48
52
  }, [gen.currentStep, t]);
49
53
 
50
54
  return (
51
55
  <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
52
- <GeneratorHeader
56
+ <GeneratorHeader
53
57
  currentStep={gen.currentStep}
54
58
  partnerName={gen.partnerName}
55
59
  onBack={gen.handleBack}
@@ -58,7 +62,10 @@ export const LoveMessageGeneratorScreen: FC<LoveMessageGeneratorScreenProps> = (
58
62
  isGenerating={gen.isGenerating}
59
63
  />
60
64
 
61
- <ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ paddingBottom: bottom + 120 }}>
65
+ <ScrollView
66
+ showsVerticalScrollIndicator={false}
67
+ contentContainerStyle={{ paddingBottom: bottom + 120 }}
68
+ >
62
69
  <View style={styles.heroSection}>
63
70
  <ProgressDots currentStep={gen.currentStep} totalSteps={4} />
64
71
  <AtomicText type="headlineLarge" color="textPrimary" style={styles.heroTitle}>
@@ -69,20 +76,20 @@ export const LoveMessageGeneratorScreen: FC<LoveMessageGeneratorScreenProps> = (
69
76
  <View style={styles.formContent}>
70
77
  {gen.currentStep === GeneratorStep.PARTNER && (
71
78
  <Animated.View>
72
- <StepPartner
79
+ <StepPartner
73
80
  partnerName={gen.partnerName}
74
81
  setPartnerName={gen.setPartnerName}
75
82
  useProfile={gen.useProfile}
76
83
  setUseProfile={gen.setUseProfile}
77
84
  hasProfile={gen.hasProfile}
78
- onEditProfile={onNavigateToProfile}
85
+ onEditProfile={handleNavigateToProfile}
79
86
  />
80
87
  </Animated.View>
81
88
  )}
82
89
 
83
90
  {gen.currentStep === GeneratorStep.VIBE && (
84
91
  <Animated.View>
85
- <StepVibe
92
+ <StepVibe
86
93
  selectedType={gen.selectedType}
87
94
  setSelectedType={gen.setSelectedType}
88
95
  selectedTone={gen.selectedTone}
@@ -120,9 +127,20 @@ export const LoveMessageGeneratorScreen: FC<LoveMessageGeneratorScreenProps> = (
120
127
 
121
128
  const styles = StyleSheet.create({
122
129
  container: { flex: 1 },
123
- heroSection: { paddingVertical: 32, alignItems: 'center' },
124
- heroTitle: { fontWeight: 'bold', textAlign: 'center', marginBottom: 8, paddingHorizontal: 20 },
130
+ heroSection: { paddingVertical: 32, alignItems: "center" },
131
+ heroTitle: {
132
+ fontWeight: "bold",
133
+ textAlign: "center",
134
+ marginBottom: 8,
135
+ paddingHorizontal: 20,
136
+ },
125
137
  formContent: { paddingHorizontal: 20 },
126
- footer: { position: 'absolute', bottom: 0, left: 0, right: 0, padding: 24, backgroundColor: 'transparent' },
138
+ footer: {
139
+ position: "absolute",
140
+ bottom: 0,
141
+ left: 0,
142
+ right: 0,
143
+ padding: 24,
144
+ },
127
145
  actionBtn: { height: 60, borderRadius: 30 },
128
146
  });
@@ -9,59 +9,67 @@ import {
9
9
  AtomicIcon,
10
10
  useAppDesignTokens,
11
11
  useSafeAreaInsets,
12
+ AppNavigation,
12
13
  } from "@umituz/react-native-design-system";
13
14
  import { useLocalization } from "@umituz/react-native-localization";
15
+ import { useRoute, RouteProp } from "@react-navigation/native";
14
16
  import { CATEGORY_TEMPLATES, MESSAGE_TYPES } from "../../domain/constants";
15
17
  import { MessageListItem } from "../components/MessageListItem";
18
+ import type { MessageType } from "../../domain/types";
16
19
 
17
- interface MessageListScreenProps {
18
- categoryId: string;
19
- onBack: () => void;
20
- onNavigateToGenerator: (initialType?: string) => void;
21
- }
20
+ type RouteParams = { categoryId?: string };
22
21
 
23
- export const MessageListScreen: FC<MessageListScreenProps> = ({
24
- categoryId,
25
- onBack,
26
- onNavigateToGenerator,
27
- }) => {
22
+ export const MessageListScreen: FC = () => {
28
23
  const tokens = useAppDesignTokens();
29
24
  const { top, bottom } = useSafeAreaInsets();
30
25
  const { t } = useLocalization();
26
+ const route = useRoute<RouteProp<{ params: RouteParams }, "params">>();
31
27
 
28
+ const categoryId = route.params?.categoryId ?? "romantic";
32
29
  const messages = useMemo(() => CATEGORY_TEMPLATES[categoryId] || [], [categoryId]);
33
30
 
34
31
  const categoryTitle = useMemo(() => {
35
32
  if (categoryId === "trending") return t("loveMessage.explore.trending");
36
- const config = MESSAGE_TYPES.find(c => c.type === categoryId);
33
+ const config = MESSAGE_TYPES.find((c) => c.type === categoryId);
37
34
  return config ? t(config.labelKey) : categoryId;
38
35
  }, [categoryId, t]);
39
36
 
37
+ const handleBack = () => AppNavigation.goBack();
38
+
39
+ const handleNavigateToGenerator = (type?: string) => {
40
+ AppNavigation.navigate("MessageGenerator", { initialType: type as MessageType });
41
+ };
42
+
40
43
  const handleShare = useCallback(async (text: string) => {
41
44
  try {
42
45
  await Share.share({ message: text });
43
- } catch { /* Ignore */ }
46
+ } catch {
47
+ /* Ignore */
48
+ }
44
49
  }, []);
45
50
 
46
51
  return (
47
52
  <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
48
53
  <View style={[styles.header, { paddingTop: top + tokens.spacing.md }]}>
49
- <Pressable onPress={onBack} style={styles.backBtn}>
54
+ <Pressable onPress={handleBack} style={styles.backBtn}>
50
55
  <AtomicIcon name="arrow-back" color="textPrimary" size="sm" />
51
56
  </Pressable>
52
57
  <AtomicText type="headlineSmall" color="textPrimary" style={styles.headerTitle}>
53
58
  {categoryTitle}
54
59
  </AtomicText>
55
- <View style={{ width: 44 }} />
60
+ <View style={styles.spacer} />
56
61
  </View>
57
62
 
58
- <ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ paddingBottom: bottom + 100, paddingHorizontal: 16 }}>
63
+ <ScrollView
64
+ showsVerticalScrollIndicator={false}
65
+ contentContainerStyle={{ paddingBottom: bottom + 100, paddingHorizontal: 16 }}
66
+ >
59
67
  <View style={styles.headlineContainer}>
60
68
  <AtomicText type="headlineMedium" color="textPrimary" style={styles.headline}>
61
- {`${messages.length} messages`}
69
+ {`${messages.length} ${t("loveMessage.messages")}`}
62
70
  </AtomicText>
63
71
  <AtomicText type="bodySmall" color="textTertiary">
64
- AI-crafted for your special moments
72
+ {t("loveMessage.aiCrafted")}
65
73
  </AtomicText>
66
74
  </View>
67
75
 
@@ -71,13 +79,13 @@ export const MessageListScreen: FC<MessageListScreenProps> = ({
71
79
  </ScrollView>
72
80
 
73
81
  <View style={[styles.fabContainer, { bottom: bottom + tokens.spacing.lg }]}>
74
- <Pressable
75
- onPress={() => onNavigateToGenerator(categoryId !== "trending" ? categoryId : undefined)}
82
+ <Pressable
83
+ onPress={() => handleNavigateToGenerator(categoryId !== "trending" ? categoryId : undefined)}
76
84
  style={[styles.fab, { backgroundColor: tokens.colors.primary }]}
77
85
  >
78
86
  <AtomicIcon name="sparkles" color="onPrimary" size="sm" />
79
87
  <AtomicText type="labelLarge" color="onPrimary" style={styles.fabText}>
80
- GENERATE MORE
88
+ {t("loveMessage.generateMore")}
81
89
  </AtomicText>
82
90
  </Pressable>
83
91
  </View>
@@ -87,12 +95,32 @@ export const MessageListScreen: FC<MessageListScreenProps> = ({
87
95
 
88
96
  const styles = StyleSheet.create({
89
97
  container: { flex: 1 },
90
- header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 8, paddingBottom: 16 },
98
+ header: {
99
+ flexDirection: "row",
100
+ justifyContent: "space-between",
101
+ alignItems: "center",
102
+ paddingHorizontal: 8,
103
+ paddingBottom: 16,
104
+ },
91
105
  backBtn: { padding: 12 },
92
- headerTitle: { fontWeight: 'bold' },
106
+ headerTitle: { fontWeight: "bold" },
107
+ spacer: { width: 44 },
93
108
  headlineContainer: { paddingVertical: 24 },
94
- headline: { fontWeight: 'bold', marginBottom: 4 },
95
- fabContainer: { position: "absolute", left: 20, right: 20, alignItems: "center" },
96
- fab: { flexDirection: "row", alignItems: "center", justifyContent: "center", paddingVertical: 16, paddingHorizontal: 24, borderRadius: 32, gap: 12 },
109
+ headline: { fontWeight: "bold", marginBottom: 4 },
110
+ fabContainer: {
111
+ position: "absolute",
112
+ left: 20,
113
+ right: 20,
114
+ alignItems: "center",
115
+ },
116
+ fab: {
117
+ flexDirection: "row",
118
+ alignItems: "center",
119
+ justifyContent: "center",
120
+ paddingVertical: 16,
121
+ paddingHorizontal: 24,
122
+ borderRadius: 32,
123
+ gap: 12,
124
+ },
97
125
  fabText: { fontWeight: "bold", letterSpacing: 1 },
98
126
  });
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * Partner Profile Screen
3
- * Optimized version with hooks and components
4
3
  */
5
4
 
6
5
  import { FC } from "react";
@@ -10,28 +9,25 @@ import {
10
9
  AtomicButton,
11
10
  useAppDesignTokens,
12
11
  useSafeAreaInsets,
12
+ AppNavigation,
13
13
  } from "@umituz/react-native-design-system";
14
14
  import { useLocalization } from "@umituz/react-native-localization";
15
15
  import { LOVE_LANGUAGES } from "../../domain/constants";
16
16
  import { usePartnerProfile } from "../hooks/usePartnerProfile";
17
17
  import { FieldInput } from "../components/FieldInput";
18
18
 
19
- interface PartnerProfileScreenProps {
20
- onBack: () => void;
21
- }
22
-
23
- export const PartnerProfileScreen: FC<PartnerProfileScreenProps> = ({ onBack }) => {
19
+ export const PartnerProfileScreen: FC = () => {
24
20
  const tokens = useAppDesignTokens();
25
21
  const { top, bottom } = useSafeAreaInsets();
26
22
  const { t } = useLocalization();
27
- const p = usePartnerProfile(onBack);
23
+ const p = usePartnerProfile(() => AppNavigation.goBack());
28
24
 
29
25
  if (p.isLoading) return null;
30
26
 
31
27
  return (
32
28
  <View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
33
29
  <View style={[styles.header, { paddingTop: top + tokens.spacing.md }]}>
34
- <AtomicButton icon="arrow-back" onPress={onBack} variant="text" size="sm" />
30
+ <AtomicButton icon="arrow-back" onPress={() => AppNavigation.goBack()} variant="text" size="sm" />
35
31
  <View style={styles.headerTitle}>
36
32
  <AtomicText type="headlineSmall" color="textPrimary" style={styles.headerText}>
37
33
  {t("loveMessage.partnerProfile.title")}
@@ -39,17 +35,21 @@ export const PartnerProfileScreen: FC<PartnerProfileScreenProps> = ({ onBack })
39
35
  </View>
40
36
  </View>
41
37
 
42
- <ScrollView
43
- contentContainerStyle={{ paddingBottom: bottom + tokens.spacing.xl, paddingHorizontal: tokens.spacing.lg }}
38
+ <ScrollView
39
+ contentContainerStyle={{
40
+ paddingBottom: bottom + tokens.spacing.xl,
41
+ paddingHorizontal: tokens.spacing.lg,
42
+ }}
44
43
  showsVerticalScrollIndicator={false}
45
44
  >
46
45
  <AtomicText type="bodyMedium" color="textSecondary" style={styles.subtitle}>
47
46
  {t("loveMessage.partnerProfile.subtitle")}
48
47
  </AtomicText>
49
48
 
50
- <FieldInput
51
- label={t("loveMessage.partnerName")} value={p.profile.name}
52
- onChange={(text) => p.setProfile((prev) => ({ ...prev, name: text }))}
49
+ <FieldInput
50
+ label={t("loveMessage.partnerName")}
51
+ value={p.profile.name}
52
+ onChange={(text) => p.setProfile((prev) => ({ ...prev, name: text }))}
53
53
  placeholder={t("loveMessage.partnerNamePlaceholder")}
54
54
  />
55
55
 
@@ -60,30 +60,39 @@ export const PartnerProfileScreen: FC<PartnerProfileScreenProps> = ({ onBack })
60
60
  <View style={styles.chipContainer}>
61
61
  {LOVE_LANGUAGES.map((lang) => (
62
62
  <AtomicButton
63
- key={lang.language} title={t(lang.labelKey)}
63
+ key={lang.language}
64
+ title={t(lang.labelKey)}
64
65
  onPress={() => p.setProfile((prev) => ({ ...prev, loveLanguage: lang.language }))}
65
66
  variant={p.profile.loveLanguage === lang.language ? "primary" : "outline"}
66
- size="sm" style={styles.chip}
67
+ size="sm"
68
+ style={styles.chip}
67
69
  />
68
70
  ))}
69
71
  </View>
70
72
  </View>
71
73
 
72
- <FieldInput
73
- label={t("loveMessage.partnerProfile.traits")} value={p.profile.traits}
74
- onChange={(text) => p.setProfile((prev) => ({ ...prev, traits: text }))}
74
+ <FieldInput
75
+ label={t("loveMessage.partnerProfile.traits")}
76
+ value={p.profile.traits}
77
+ onChange={(text) => p.setProfile((prev) => ({ ...prev, traits: text }))}
75
78
  placeholder={t("loveMessage.partnerProfile.traitsPlaceholder")}
76
79
  />
77
80
 
78
- <FieldInput
79
- label={t("loveMessage.partnerProfile.quirks")} value={p.profile.quirks}
80
- onChange={(text) => p.setProfile((prev) => ({ ...prev, quirks: text }))}
81
- placeholder={t("loveMessage.partnerProfile.quirksPlaceholder")} multiline
81
+ <FieldInput
82
+ label={t("loveMessage.partnerProfile.quirks")}
83
+ value={p.profile.quirks}
84
+ onChange={(text) => p.setProfile((prev) => ({ ...prev, quirks: text }))}
85
+ placeholder={t("loveMessage.partnerProfile.quirksPlaceholder")}
86
+ multiline
82
87
  />
83
88
 
84
89
  <AtomicButton
85
- title={t("loveMessage.partnerProfile.save")} onPress={p.handleSave}
86
- disabled={!p.profile.name.trim()} variant="primary" fullWidth style={styles.saveBtn}
90
+ title={t("loveMessage.partnerProfile.save")}
91
+ onPress={p.handleSave}
92
+ disabled={!p.profile.name.trim()}
93
+ variant="primary"
94
+ fullWidth
95
+ style={styles.saveBtn}
87
96
  />
88
97
  </ScrollView>
89
98
  </View>
@@ -92,7 +101,12 @@ export const PartnerProfileScreen: FC<PartnerProfileScreenProps> = ({ onBack })
92
101
 
93
102
  const styles = StyleSheet.create({
94
103
  container: { flex: 1 },
95
- header: { flexDirection: "row", alignItems: "center", paddingHorizontal: 8, paddingBottom: 24 },
104
+ header: {
105
+ flexDirection: "row",
106
+ alignItems: "center",
107
+ paddingHorizontal: 8,
108
+ paddingBottom: 24,
109
+ },
96
110
  headerTitle: { flex: 1, marginRight: 40, alignItems: "center" },
97
111
  headerText: { fontWeight: "bold" },
98
112
  subtitle: { marginBottom: 32 },