@umituz/react-native-settings 4.23.84 → 4.23.85
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/domains/about/presentation/hooks/useAboutInfo.ts +0 -2
- package/src/domains/appearance/presentation/components/ColorPicker.tsx +0 -1
- package/src/domains/appearance/presentation/components/ThemeModeSection.tsx +0 -1
- package/src/domains/appearance/presentation/components/ThemeOption.tsx +0 -1
- package/src/domains/feedback/presentation/components/FeedbackForm.tsx +0 -1
- package/src/domains/feedback/presentation/components/FeedbackModal.tsx +12 -12
- package/src/domains/feedback/presentation/components/SupportSection.tsx +0 -1
- package/src/domains/localization/infrastructure/components/useLanguageSwitcher.ts +0 -1
- package/src/domains/localization/infrastructure/hooks/useLanguageSelection.ts +0 -4
- package/src/domains/localization/infrastructure/hooks/useTranslation.ts +0 -1
- package/src/domains/localization/infrastructure/storage/LanguageInitializer.ts +0 -1
- package/src/domains/localization/infrastructure/storage/LanguageSwitcher.ts +0 -4
- package/src/domains/localization/infrastructure/storage/LocalizationStore.ts +0 -5
- package/src/domains/localization/presentation/components/LanguageItem.tsx +0 -1
- package/src/domains/localization/presentation/providers/LocalizationManager.tsx +0 -1
- package/src/domains/localization/presentation/screens/LanguageSelectionScreen.tsx +0 -3
- package/src/domains/notifications/infrastructure/services/NotificationService.ts +0 -1
- package/src/domains/notifications/infrastructure/utils/dev.ts +0 -3
- package/src/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.ts +0 -1
- package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +0 -6
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +0 -5
- package/src/domains/rating/application/services/RatingService.ts +13 -63
- package/src/domains/rating/infrastructure/storage/RatingStorage.ts +14 -36
- package/src/domains/rating/presentation/hooks/useAppRating.tsx +0 -1
- package/src/infrastructure/utils/dateUtils.ts +61 -0
- package/src/infrastructure/utils/memoComparisonUtils.ts +68 -0
- package/src/infrastructure/utils/sanitizers.ts +49 -0
- package/src/infrastructure/utils/translationHelpers.ts +81 -0
- package/src/infrastructure/utils/validators.ts +59 -0
- package/src/presentation/components/SettingsItemCard.tsx +129 -172
- package/src/presentation/components/settings/SettingsItemCardContent.tsx +70 -0
- package/src/presentation/components/settings/SettingsItemCardRightElement.tsx +42 -0
- package/src/presentation/components/settings/SettingsItemCardSection.tsx +29 -0
- package/src/presentation/hooks/useSettingsScreenConfig.ts +19 -54
- package/src/presentation/navigation/hooks/useSettingsScreens.ts +75 -89
- package/src/presentation/screens/components/GamificationSettingsItem.tsx +20 -17
- package/src/presentation/screens/components/SettingsContent.tsx +29 -0
- package/src/presentation/screens/components/SubscriptionSettingsItem.tsx +13 -4
- package/src/presentation/screens/components/VideoTutorialSettingsItem.tsx +47 -0
- package/src/presentation/screens/components/WalletSettingsItem.tsx +13 -4
- package/src/presentation/screens/components/sections/CustomSettingsList.tsx +12 -5
- package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +12 -11
- package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +4 -1
- package/src/presentation/screens/components/sections/ProfileSectionLoader.tsx +16 -10
- package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +9 -4
- package/src/presentation/screens/hooks/useFeatureDetection.ts +2 -0
- package/src/presentation/screens/types/SettingsConfig.ts +8 -1
- package/src/presentation/screens/types/UserFeatureConfig.ts +20 -0
- package/src/presentation/screens/types/index.ts +1 -0
- package/src/presentation/screens/utils/normalizeConfig.ts +7 -1
- package/src/presentation/utils/accountConfigUtils.ts +67 -0
- package/src/presentation/utils/screenFactory.ts +87 -0
- package/src/presentation/utils/userProfileUtils.ts +51 -0
|
@@ -2,14 +2,13 @@ import React, { useMemo } from "react";
|
|
|
2
2
|
import { SettingsSection } from "../../../components/SettingsSection";
|
|
3
3
|
import { SettingsItemCard } from "../../../components/SettingsItemCard";
|
|
4
4
|
import type { CustomSettingsSection } from "../../types";
|
|
5
|
+
import { createSinglePropComparator } from "../../../../infrastructure/utils/memoComparisonUtils";
|
|
5
6
|
|
|
6
7
|
interface CustomSettingsListProps {
|
|
7
8
|
customSections?: CustomSettingsSection[];
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({
|
|
11
|
-
customSections = [],
|
|
12
|
-
}) => {
|
|
11
|
+
export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({ customSections = [] }) => {
|
|
13
12
|
const sortedSections = useMemo(() => {
|
|
14
13
|
return Array.from(customSections)
|
|
15
14
|
.sort((a: CustomSettingsSection, b: CustomSettingsSection) => (a.order ?? 999) - (b.order ?? 999));
|
|
@@ -25,9 +24,9 @@ export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({
|
|
|
25
24
|
title={section.title}
|
|
26
25
|
>
|
|
27
26
|
{section.content}
|
|
28
|
-
{!section.content && section.items && section.items.length > 0 && section.items.map((item) => (
|
|
27
|
+
{!section.content && section.items && section.items.length > 0 && section.items.map((item, itemIndex) => (
|
|
29
28
|
<SettingsItemCard
|
|
30
|
-
key={item.id || `item-${
|
|
29
|
+
key={item.id || `item-${itemIndex}`}
|
|
31
30
|
title={item.title}
|
|
32
31
|
description={item.subtitle}
|
|
33
32
|
icon={item.icon}
|
|
@@ -42,3 +41,11 @@ export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({
|
|
|
42
41
|
</>
|
|
43
42
|
);
|
|
44
43
|
};
|
|
44
|
+
|
|
45
|
+
CustomSettingsList.displayName = "CustomSettingsList";
|
|
46
|
+
|
|
47
|
+
export const MemoizedCustomSettingsList = React.memo(
|
|
48
|
+
CustomSettingsList,
|
|
49
|
+
createSinglePropComparator("customSections")
|
|
50
|
+
);
|
|
51
|
+
MemoizedCustomSettingsList.displayName = "MemoizedCustomSettingsList";
|
|
@@ -5,8 +5,8 @@ import { NotificationsSection } from "../../../../domains/notifications";
|
|
|
5
5
|
import { useLocalization, getLanguageByCode } from "../../../../domains/localization";
|
|
6
6
|
import { SettingsItemCard } from "../../../components/SettingsItemCard";
|
|
7
7
|
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|
|
8
|
-
|
|
9
8
|
import { SettingsSection } from "../../../components/SettingsSection";
|
|
9
|
+
import { compareConfigAndFeatures } from "../../../../infrastructure/utils/memoComparisonUtils";
|
|
10
10
|
|
|
11
11
|
interface FeatureSettingsSectionProps {
|
|
12
12
|
normalizedConfig: NormalizedConfig;
|
|
@@ -24,23 +24,22 @@ export const FeatureSettingsSection: React.FC<FeatureSettingsSectionProps> = ({
|
|
|
24
24
|
const { t, currentLanguage } = useLocalization();
|
|
25
25
|
const navigation = useAppNavigation();
|
|
26
26
|
|
|
27
|
-
const handleLanguagePress = () => {
|
|
27
|
+
const handleLanguagePress = React.useCallback(() => {
|
|
28
28
|
if (normalizedConfig.language.config?.onPress) {
|
|
29
29
|
normalizedConfig.language.config.onPress();
|
|
30
30
|
} else {
|
|
31
|
-
const route =
|
|
32
|
-
normalizedConfig.language.config?.route || "LanguageSelection";
|
|
31
|
+
const route = normalizedConfig.language.config?.route || "LanguageSelection";
|
|
33
32
|
navigation.navigate(route as never);
|
|
34
33
|
}
|
|
35
|
-
};
|
|
34
|
+
}, [navigation, normalizedConfig.language.config]);
|
|
36
35
|
|
|
37
|
-
const currentLanguageData = getLanguageByCode(currentLanguage);
|
|
38
|
-
const languageDisplayName =
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
const currentLanguageData = React.useMemo(() => getLanguageByCode(currentLanguage), [currentLanguage]);
|
|
37
|
+
const languageDisplayName = React.useMemo(() => {
|
|
38
|
+
if (!currentLanguageData) return currentLanguage;
|
|
39
|
+
return `${currentLanguageData.flag} ${currentLanguageData.nativeName}`;
|
|
40
|
+
}, [currentLanguageData, currentLanguage]);
|
|
41
41
|
|
|
42
|
-
if (!features.appearance && !features.language && !features.notifications)
|
|
43
|
-
return null;
|
|
42
|
+
if (!features.appearance && !features.language && !features.notifications) return null;
|
|
44
43
|
|
|
45
44
|
return (
|
|
46
45
|
<SettingsSection
|
|
@@ -85,3 +84,5 @@ export const FeatureSettingsSection: React.FC<FeatureSettingsSectionProps> = ({
|
|
|
85
84
|
);
|
|
86
85
|
};
|
|
87
86
|
|
|
87
|
+
export const MemoizedFeatureSettingsSection = React.memo(FeatureSettingsSection, compareConfigAndFeatures);
|
|
88
|
+
MemoizedFeatureSettingsSection.displayName = "MemoizedFeatureSettingsSection";
|
|
@@ -3,8 +3,8 @@ import { AboutSection } from "../../../../domains/about/presentation/components/
|
|
|
3
3
|
import { LegalSection } from "../../../../domains/legal/presentation/components/LegalSection";
|
|
4
4
|
import { useLocalization } from "../../../../domains/localization";
|
|
5
5
|
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|
|
6
|
-
|
|
7
6
|
import { SettingsSection } from "../../../components/SettingsSection";
|
|
7
|
+
import { compareConfigAndFeatures } from "../../../../infrastructure/utils/memoComparisonUtils";
|
|
8
8
|
|
|
9
9
|
interface IdentitySettingsSectionProps {
|
|
10
10
|
normalizedConfig: NormalizedConfig;
|
|
@@ -48,4 +48,7 @@ export const IdentitySettingsSection: React.FC<IdentitySettingsSectionProps> = (
|
|
|
48
48
|
);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
IdentitySettingsSection.displayName = "IdentitySettingsSection";
|
|
51
52
|
|
|
53
|
+
export const MemoizedIdentitySettingsSection = React.memo(IdentitySettingsSection, compareConfigAndFeatures);
|
|
54
|
+
MemoizedIdentitySettingsSection.displayName = "MemoizedIdentitySettingsSection";
|
|
@@ -3,6 +3,7 @@ import { View, StyleSheet } from "react-native";
|
|
|
3
3
|
import { ProfileSection } from "@umituz/react-native-auth";
|
|
4
4
|
import { useLocalization } from "../../../../domains/localization";
|
|
5
5
|
import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
6
|
+
import { createSinglePropComparator } from "../../../../infrastructure/utils/memoComparisonUtils";
|
|
6
7
|
|
|
7
8
|
export interface ProfileSectionLoaderProps {
|
|
8
9
|
userProfile?: {
|
|
@@ -16,24 +17,27 @@ export interface ProfileSectionLoaderProps {
|
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
export const ProfileSectionLoader: React.FC<ProfileSectionLoaderProps> = ({ userProfile }) => {
|
|
20
|
+
export const ProfileSectionLoader: React.FC<ProfileSectionLoaderProps> = React.memo(({ userProfile }) => {
|
|
21
21
|
const { t } = useLocalization();
|
|
22
22
|
const navigation = useAppNavigation();
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const handlePress = () => {
|
|
24
|
+
const handlePress = React.useCallback(() => {
|
|
25
|
+
if (!userProfile) return;
|
|
27
26
|
if (userProfile.onPress) {
|
|
28
27
|
userProfile.onPress();
|
|
29
28
|
} else if (userProfile.accountSettingsRoute) {
|
|
30
29
|
navigation.navigate(userProfile.accountSettingsRoute);
|
|
31
30
|
}
|
|
32
|
-
};
|
|
31
|
+
}, [navigation, userProfile]);
|
|
33
32
|
|
|
34
|
-
const anonymousDisplayName =
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const anonymousDisplayName = React.useMemo(() => {
|
|
34
|
+
if (!userProfile) return "";
|
|
35
|
+
return userProfile.isAnonymous && userProfile.userId
|
|
36
|
+
? `${t("profile.guest", "Guest")} ${userProfile.userId.substring(0, 8)}`
|
|
37
|
+
: t("settings.profile.anonymousName", "Anonymous User");
|
|
38
|
+
}, [userProfile, t]);
|
|
39
|
+
|
|
40
|
+
if (!userProfile) return null;
|
|
37
41
|
|
|
38
42
|
return (
|
|
39
43
|
<View style={styles.profileContainer}>
|
|
@@ -53,7 +57,9 @@ export const ProfileSectionLoader: React.FC<ProfileSectionLoaderProps> = ({ user
|
|
|
53
57
|
/>
|
|
54
58
|
</View>
|
|
55
59
|
);
|
|
56
|
-
};
|
|
60
|
+
}, createSinglePropComparator("userProfile"));
|
|
61
|
+
|
|
62
|
+
ProfileSectionLoader.displayName = "ProfileSectionLoader";
|
|
57
63
|
|
|
58
64
|
const styles = StyleSheet.create({
|
|
59
65
|
profileContainer: {
|
|
@@ -5,6 +5,7 @@ import { SupportSection } from "../../../../domains/feedback/presentation/compon
|
|
|
5
5
|
import { SettingsSection } from "../../../components/SettingsSection";
|
|
6
6
|
import { SettingsItemCard } from "../../../components/SettingsItemCard";
|
|
7
7
|
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|
|
8
|
+
import { compareConfigAndFeatures } from "../../../../infrastructure/utils/memoComparisonUtils";
|
|
8
9
|
|
|
9
10
|
interface SupportSettingsSectionProps {
|
|
10
11
|
features: { feedback: boolean; rating: boolean; faqs: boolean; [key: string]: boolean };
|
|
@@ -30,10 +31,10 @@ export const SupportSettingsSection: React.FC<SupportSettingsSectionProps> = ({
|
|
|
30
31
|
<SupportSection
|
|
31
32
|
renderSection={(props: { title: string; children: React.ReactNode }) => <>{props.children}</>}
|
|
32
33
|
renderItem={(props: { title: string; icon: string; onPress: () => void; isLast?: boolean }) => (
|
|
33
|
-
<SettingsItemCard
|
|
34
|
-
title={props.title}
|
|
35
|
-
icon={props.icon}
|
|
36
|
-
onPress={props.onPress}
|
|
34
|
+
<SettingsItemCard
|
|
35
|
+
title={props.title}
|
|
36
|
+
icon={props.icon}
|
|
37
|
+
onPress={props.onPress}
|
|
37
38
|
noBackground={true}
|
|
38
39
|
hideMargin={true}
|
|
39
40
|
/>
|
|
@@ -98,3 +99,7 @@ export const SupportSettingsSection: React.FC<SupportSettingsSectionProps> = ({
|
|
|
98
99
|
);
|
|
99
100
|
};
|
|
100
101
|
|
|
102
|
+
SupportSettingsSection.displayName = "SupportSettingsSection";
|
|
103
|
+
|
|
104
|
+
export const MemoizedSupportSettingsSection = React.memo(SupportSettingsSection, compareConfigAndFeatures);
|
|
105
|
+
MemoizedSupportSettingsSection.displayName = "MemoizedSupportSettingsSection";
|
|
@@ -51,6 +51,7 @@ export function useFeatureDetection(
|
|
|
51
51
|
subscription,
|
|
52
52
|
wallet,
|
|
53
53
|
gamification,
|
|
54
|
+
videoTutorial,
|
|
54
55
|
} = normalizedConfig;
|
|
55
56
|
|
|
56
57
|
const notificationServiceAvailable = !!options?.notificationServiceAvailable;
|
|
@@ -118,6 +119,7 @@ export function useFeatureDetection(
|
|
|
118
119
|
subscription: subscription.enabled,
|
|
119
120
|
wallet: wallet.enabled,
|
|
120
121
|
gamification: gamification.enabled,
|
|
122
|
+
videoTutorial: videoTutorial.enabled,
|
|
121
123
|
};
|
|
122
124
|
}, [normalizedConfig, navigation, options]);
|
|
123
125
|
}
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
SubscriptionConfig,
|
|
22
22
|
WalletConfig,
|
|
23
23
|
GamificationItemConfig,
|
|
24
|
+
VideoTutorialConfig,
|
|
24
25
|
} from "./UserFeatureConfig";
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -133,10 +134,16 @@ export interface SettingsConfig {
|
|
|
133
134
|
|
|
134
135
|
/**
|
|
135
136
|
* Gamification settings configuration
|
|
136
|
-
* @default
|
|
137
|
+
* @default 'auto'
|
|
137
138
|
*/
|
|
138
139
|
gamification?: FeatureVisibility | GamificationItemConfig;
|
|
139
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Video tutorial settings configuration
|
|
143
|
+
* @default 'auto'
|
|
144
|
+
*/
|
|
145
|
+
videoTutorial?: FeatureVisibility | VideoTutorialConfig;
|
|
146
|
+
|
|
140
147
|
/**
|
|
141
148
|
* Custom empty state text when no settings are available
|
|
142
149
|
*/
|
|
@@ -161,3 +161,23 @@ export interface GamificationItemConfig {
|
|
|
161
161
|
achievementsCount?: number;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Video Tutorial Settings Configuration
|
|
166
|
+
*/
|
|
167
|
+
export interface VideoTutorialConfig {
|
|
168
|
+
/** Enable video tutorial feature */
|
|
169
|
+
enabled?: FeatureVisibility;
|
|
170
|
+
/** Custom title for the video tutorial section */
|
|
171
|
+
title?: string;
|
|
172
|
+
/** Custom label for the video tutorial item */
|
|
173
|
+
description?: string;
|
|
174
|
+
/** Custom icon name (Ionicons) */
|
|
175
|
+
icon?: string;
|
|
176
|
+
/** Custom section title for grouping */
|
|
177
|
+
sectionTitle?: string;
|
|
178
|
+
/** Handler to open video tutorial screen */
|
|
179
|
+
onPress?: () => void;
|
|
180
|
+
/** Navigation route for video tutorial screen */
|
|
181
|
+
route?: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
@@ -21,6 +21,7 @@ export type {
|
|
|
21
21
|
SubscriptionConfig,
|
|
22
22
|
WalletConfig,
|
|
23
23
|
GamificationItemConfig,
|
|
24
|
+
VideoTutorialConfig,
|
|
24
25
|
} from "./UserFeatureConfig";
|
|
25
26
|
export type { GamificationSettingsConfig as GamificationConfig } from "../../../domains/gamification";
|
|
26
27
|
export type { SettingsConfig } from "./SettingsConfig";
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
SubscriptionConfig,
|
|
19
19
|
WalletConfig,
|
|
20
20
|
GamificationItemConfig,
|
|
21
|
+
VideoTutorialConfig,
|
|
21
22
|
SettingsConfig,
|
|
22
23
|
} from "../types";
|
|
23
24
|
|
|
@@ -74,6 +75,10 @@ export interface NormalizedConfig {
|
|
|
74
75
|
enabled: boolean;
|
|
75
76
|
config?: GamificationItemConfig;
|
|
76
77
|
};
|
|
78
|
+
videoTutorial: {
|
|
79
|
+
enabled: boolean;
|
|
80
|
+
config?: VideoTutorialConfig;
|
|
81
|
+
};
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
/**
|
|
@@ -121,6 +126,7 @@ export function normalizeSettingsConfig(
|
|
|
121
126
|
faqs: normalizeConfigValue(config?.faqs, false),
|
|
122
127
|
subscription: normalizeConfigValue(config?.subscription, false),
|
|
123
128
|
wallet: normalizeConfigValue(config?.wallet, false),
|
|
124
|
-
gamification: normalizeConfigValue(config?.gamification,
|
|
129
|
+
gamification: normalizeConfigValue(config?.gamification, "auto"),
|
|
130
|
+
videoTutorial: normalizeConfigValue(config?.videoTutorial, "auto"),
|
|
125
131
|
};
|
|
126
132
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account Configuration Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for creating account screen configurations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { AccountScreenConfig } from "@umituz/react-native-auth";
|
|
8
|
+
|
|
9
|
+
export interface CreateAccountConfigParams {
|
|
10
|
+
displayName?: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
photoURL?: string;
|
|
13
|
+
isAnonymous?: boolean;
|
|
14
|
+
avatarUrl?: string;
|
|
15
|
+
onSignIn: () => void;
|
|
16
|
+
onLogout: () => Promise<void>;
|
|
17
|
+
onDeleteAccount: () => Promise<void>;
|
|
18
|
+
t: (key: string) => string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create account screen configuration
|
|
23
|
+
*/
|
|
24
|
+
export function createAccountConfig(params: CreateAccountConfigParams): AccountScreenConfig {
|
|
25
|
+
const {
|
|
26
|
+
displayName,
|
|
27
|
+
userId,
|
|
28
|
+
photoURL,
|
|
29
|
+
isAnonymous,
|
|
30
|
+
avatarUrl,
|
|
31
|
+
onSignIn,
|
|
32
|
+
onLogout,
|
|
33
|
+
onDeleteAccount,
|
|
34
|
+
t,
|
|
35
|
+
} = params;
|
|
36
|
+
|
|
37
|
+
const anonymous = isAnonymous ?? true;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
profile: {
|
|
41
|
+
displayName: displayName || t("settings.profile.anonymousName"),
|
|
42
|
+
userId: userId ?? undefined,
|
|
43
|
+
isAnonymous: anonymous,
|
|
44
|
+
avatarUrl: avatarUrl ?? photoURL ?? undefined,
|
|
45
|
+
benefits: anonymous ? [
|
|
46
|
+
t("settings.profile.benefits.saveHistory"),
|
|
47
|
+
t("settings.profile.benefits.syncDevices"),
|
|
48
|
+
t("settings.profile.benefits.cloudSync"),
|
|
49
|
+
t("settings.profile.benefits.secureBackup"),
|
|
50
|
+
] : undefined,
|
|
51
|
+
},
|
|
52
|
+
isAnonymous: anonymous,
|
|
53
|
+
editProfileText: t("settings.account.editProfile"),
|
|
54
|
+
onSignIn,
|
|
55
|
+
accountActions: {
|
|
56
|
+
onLogout,
|
|
57
|
+
onDeleteAccount,
|
|
58
|
+
logoutText: t("settings.account.logout"),
|
|
59
|
+
logoutConfirmTitle: t("settings.account.logoutConfirmTitle"),
|
|
60
|
+
logoutConfirmMessage: t("settings.account.logoutConfirmMessage"),
|
|
61
|
+
cancelText: t("common.cancel"),
|
|
62
|
+
deleteAccountText: t("settings.account.deleteAccount"),
|
|
63
|
+
deleteConfirmTitle: t("settings.account.deleteConfirmTitle"),
|
|
64
|
+
deleteConfirmMessage: t("settings.account.deleteConfirmMessage"),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screen Factory Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for creating stack screen configurations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import type { StackScreen } from "@umituz/react-native-design-system";
|
|
9
|
+
import type { AdditionalScreen } from "../navigation/types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a basic stack screen configuration
|
|
13
|
+
*/
|
|
14
|
+
export function createStackScreen(
|
|
15
|
+
name: string,
|
|
16
|
+
componentOrChildren: React.ComponentType<any> | (() => React.ReactElement),
|
|
17
|
+
options: { headerShown?: boolean } = {}
|
|
18
|
+
): StackScreen {
|
|
19
|
+
const isChildrenFunction = typeof componentOrChildren === "function" &&
|
|
20
|
+
!(componentOrChildren.prototype && componentOrChildren.prototype.isReactComponent);
|
|
21
|
+
|
|
22
|
+
if (isChildrenFunction) {
|
|
23
|
+
return {
|
|
24
|
+
name,
|
|
25
|
+
options: { headerShown: false, ...options },
|
|
26
|
+
children: componentOrChildren as () => React.ReactElement,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
name,
|
|
31
|
+
component: componentOrChildren as any,
|
|
32
|
+
options: { headerShown: false, ...options },
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create a screen with props
|
|
38
|
+
*/
|
|
39
|
+
export function createScreenWithProps<P extends Record<string, unknown>>(
|
|
40
|
+
name: string,
|
|
41
|
+
component: React.ComponentType<P>,
|
|
42
|
+
props: P,
|
|
43
|
+
options: { headerShown?: boolean } = {}
|
|
44
|
+
): StackScreen {
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
options: { headerShown: false, ...options },
|
|
48
|
+
children: (() => React.createElement(component, props)) as () => React.ReactElement,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Convert additional screen to stack screen
|
|
54
|
+
*/
|
|
55
|
+
export function convertAdditionalScreen(screen: AdditionalScreen): StackScreen {
|
|
56
|
+
const stackScreen: any = { name: screen.name };
|
|
57
|
+
if (screen.component) stackScreen.component = screen.component;
|
|
58
|
+
if (screen.children) stackScreen.children = screen.children;
|
|
59
|
+
if (screen.options) stackScreen.options = screen.options;
|
|
60
|
+
return stackScreen as StackScreen;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create conditional screen
|
|
65
|
+
*/
|
|
66
|
+
export function createConditionalScreen(
|
|
67
|
+
condition: boolean,
|
|
68
|
+
screenFactory: () => StackScreen
|
|
69
|
+
): StackScreen | null {
|
|
70
|
+
return condition ? screenFactory() : null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Combine screens excluding null values
|
|
75
|
+
*/
|
|
76
|
+
export function combineScreens(...screens: (StackScreen | null | StackScreen[])[]): StackScreen[] {
|
|
77
|
+
const result: StackScreen[] = [];
|
|
78
|
+
for (const screen of screens) {
|
|
79
|
+
if (screen === null) continue;
|
|
80
|
+
if (Array.isArray(screen)) {
|
|
81
|
+
result.push(...screen);
|
|
82
|
+
} else {
|
|
83
|
+
result.push(screen);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Profile Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for creating user profile configurations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { UserProfileDisplay } from "../navigation/types";
|
|
8
|
+
|
|
9
|
+
export interface UserProfileConfig {
|
|
10
|
+
displayName?: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
isAnonymous?: boolean;
|
|
13
|
+
avatarUrl?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CreateUserProfileParams {
|
|
17
|
+
profileData?: UserProfileConfig;
|
|
18
|
+
t: (key: string, params?: Record<string, string | number>) => string;
|
|
19
|
+
onSignIn?: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create user profile display configuration
|
|
24
|
+
*/
|
|
25
|
+
export function createUserProfileDisplay(params: CreateUserProfileParams): UserProfileDisplay {
|
|
26
|
+
const { profileData, t, onSignIn } = params;
|
|
27
|
+
|
|
28
|
+
const isAnonymous = profileData?.isAnonymous ?? true;
|
|
29
|
+
const anonymousName = t("settings.profile.anonymousName");
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
displayName: profileData?.displayName || anonymousName,
|
|
33
|
+
userId: profileData?.userId ?? undefined,
|
|
34
|
+
isAnonymous,
|
|
35
|
+
avatarUrl: profileData?.avatarUrl ?? undefined,
|
|
36
|
+
onPress: isAnonymous ? onSignIn : undefined,
|
|
37
|
+
accountSettingsRoute: isAnonymous ? undefined : "Account",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create benefits list for anonymous users
|
|
43
|
+
*/
|
|
44
|
+
export function createAnonymousBenefits(t: (key: string) => string): string[] {
|
|
45
|
+
return [
|
|
46
|
+
t("settings.profile.benefits.saveHistory"),
|
|
47
|
+
t("settings.profile.benefits.syncDevices"),
|
|
48
|
+
t("settings.profile.benefits.cloudSync"),
|
|
49
|
+
t("settings.profile.benefits.secureBackup"),
|
|
50
|
+
];
|
|
51
|
+
}
|