@umituz/react-native-settings 4.23.85 → 4.23.87
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 +3 -3
- package/src/domains/about/presentation/hooks/useAboutInfo.ts +1 -1
- package/src/domains/faqs/presentation/screens/FAQScreen.tsx +1 -1
- package/src/domains/feedback/presentation/components/FeedbackForm.styles.ts +1 -1
- package/src/domains/feedback/presentation/components/FeedbackForm.tsx +11 -4
- package/src/domains/gamification/components/GamificationScreen/GamificationScreen.tsx +1 -6
- package/src/domains/gamification/store/gamificationStore.ts +6 -7
- package/src/domains/localization/infrastructure/storage/LocalizationStore.ts +50 -181
- package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +182 -0
- package/src/domains/notifications/infrastructure/utils/dev.ts +3 -3
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +1 -1
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +52 -46
- package/src/infrastructure/types/commonComponentTypes.ts +142 -0
- package/src/infrastructure/utils/async/core.ts +109 -0
- package/src/infrastructure/utils/async/debounceAndBatch.ts +69 -0
- package/src/infrastructure/utils/async/index.ts +8 -0
- package/src/infrastructure/utils/async/retryAndTimeout.ts +57 -0
- package/src/infrastructure/utils/configFactory.ts +101 -0
- package/src/infrastructure/utils/errorHandlers.ts +249 -0
- package/src/infrastructure/utils/index.ts +5 -0
- package/src/infrastructure/utils/memoComparisonUtils.ts +0 -2
- package/src/infrastructure/utils/memoUtils.ts +10 -2
- package/src/infrastructure/utils/styleTokens.ts +132 -0
- package/src/infrastructure/utils/validation/core.ts +42 -0
- package/src/infrastructure/utils/validation/formValidators.ts +82 -0
- package/src/infrastructure/utils/validation/index.ts +37 -0
- package/src/infrastructure/utils/validation/numericValidators.ts +66 -0
- package/src/infrastructure/utils/validation/passwordValidator.ts +53 -0
- package/src/infrastructure/utils/validation/textValidators.ts +118 -0
- package/src/presentation/hooks/useSettingsScreenConfig.ts +32 -79
- package/src/presentation/navigation/SettingsStackNavigator.tsx +1 -24
- package/src/presentation/navigation/hooks/useSettingsScreens.ts +1 -1
- package/src/presentation/utils/config-creators/base-configs.ts +54 -42
- package/src/presentation/utils/faqTranslator.ts +31 -0
- package/src/presentation/utils/index.ts +6 -1
- package/src/presentation/utils/screenFactory.ts +1 -1
- package/src/presentation/utils/settingsConfigFactory.ts +89 -0
- package/src/presentation/utils/useAuthHandlers.ts +98 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text Validators
|
|
3
|
+
* Validation functions for text-based inputs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ValidationResult, TextValidationOptions } from "./core";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validates a text field based on provided options
|
|
10
|
+
*/
|
|
11
|
+
export const validateTextField = (
|
|
12
|
+
value: string,
|
|
13
|
+
options: TextValidationOptions = {}
|
|
14
|
+
): ValidationResult => {
|
|
15
|
+
const {
|
|
16
|
+
minLength,
|
|
17
|
+
maxLength,
|
|
18
|
+
required = false,
|
|
19
|
+
pattern,
|
|
20
|
+
customValidator,
|
|
21
|
+
} = options;
|
|
22
|
+
|
|
23
|
+
// Check required
|
|
24
|
+
if (required && !value.trim()) {
|
|
25
|
+
return { isValid: false, error: "This field is required" };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Skip other validations if empty and not required
|
|
29
|
+
if (!value.trim() && !required) {
|
|
30
|
+
return { isValid: true };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check min length
|
|
34
|
+
if (minLength && value.length < minLength) {
|
|
35
|
+
return { isValid: false, error: `Minimum length is ${minLength} characters` };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check max length
|
|
39
|
+
if (maxLength && value.length > maxLength) {
|
|
40
|
+
return { isValid: false, error: `Maximum length is ${maxLength} characters` };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check pattern
|
|
44
|
+
if (pattern && !pattern.test(value)) {
|
|
45
|
+
return { isValid: false, error: "Invalid format" };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Custom validator
|
|
49
|
+
if (customValidator) {
|
|
50
|
+
const customError = customValidator(value);
|
|
51
|
+
if (customError) {
|
|
52
|
+
return { isValid: false, error: customError };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { isValid: true };
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Validates an email address
|
|
61
|
+
*/
|
|
62
|
+
export const validateEmail = (
|
|
63
|
+
email: string,
|
|
64
|
+
options: { required?: boolean; allowEmpty?: boolean } = {}
|
|
65
|
+
): ValidationResult => {
|
|
66
|
+
const { required = false, allowEmpty = false } = options;
|
|
67
|
+
|
|
68
|
+
// Check if empty
|
|
69
|
+
if (!email.trim()) {
|
|
70
|
+
if (required && !allowEmpty) {
|
|
71
|
+
return { isValid: false, error: "Email is required" };
|
|
72
|
+
}
|
|
73
|
+
return { isValid: true };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Email regex pattern
|
|
77
|
+
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
78
|
+
if (!emailPattern.test(email)) {
|
|
79
|
+
return { isValid: false, error: "Invalid email address" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { isValid: true };
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validates a URL
|
|
87
|
+
*/
|
|
88
|
+
export const validateUrl = (url: string): ValidationResult => {
|
|
89
|
+
if (!url.trim()) {
|
|
90
|
+
return { isValid: true };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
new URL(url);
|
|
95
|
+
return { isValid: true };
|
|
96
|
+
} catch {
|
|
97
|
+
return { isValid: false, error: "Invalid URL" };
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Validates a phone number (basic validation)
|
|
103
|
+
*/
|
|
104
|
+
export const validatePhoneNumber = (phone: string): ValidationResult => {
|
|
105
|
+
if (!phone.trim()) {
|
|
106
|
+
return { isValid: true };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Remove all non-digit characters
|
|
110
|
+
const digitsOnly = phone.replace(/\D/g, "");
|
|
111
|
+
|
|
112
|
+
// Check if length is reasonable (10-15 digits)
|
|
113
|
+
if (digitsOnly.length < 10 || digitsOnly.length > 15) {
|
|
114
|
+
return { isValid: false, error: "Invalid phone number" };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { isValid: true };
|
|
118
|
+
};
|
|
@@ -6,29 +6,14 @@
|
|
|
6
6
|
* Apps pass subscription config from subscription package.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { useMemo
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
useAuth,
|
|
13
|
-
useAccountManagement,
|
|
14
|
-
useAuthModalStore,
|
|
15
|
-
useUserProfile,
|
|
16
|
-
} from "@umituz/react-native-auth";
|
|
17
|
-
import { AlertService } from "@umituz/react-native-design-system";
|
|
9
|
+
import { useMemo } from "react";
|
|
10
|
+
import { useAuth, useUserProfile } from "@umituz/react-native-auth";
|
|
18
11
|
import { useLocalization } from "../../domains/localization";
|
|
19
|
-
import {
|
|
20
|
-
createAppearanceConfig,
|
|
21
|
-
createLanguageConfig,
|
|
22
|
-
createNotificationsConfig,
|
|
23
|
-
createAboutConfig,
|
|
24
|
-
createLegalConfig,
|
|
25
|
-
createFeedbackConfig,
|
|
26
|
-
createRatingConfig,
|
|
27
|
-
createFAQConfig,
|
|
28
|
-
createSubscriptionConfig,
|
|
29
|
-
} from "../utils/config-creators";
|
|
30
12
|
import { createUserProfileDisplay } from "../utils/userProfileUtils";
|
|
31
13
|
import { createAccountConfig } from "../utils/accountConfigUtils";
|
|
14
|
+
import { useAuthHandlers } from "../utils/useAuthHandlers";
|
|
15
|
+
import { translateFAQData } from "../utils/faqTranslator";
|
|
16
|
+
import { useSettingsConfigFactory } from "../utils/settingsConfigFactory";
|
|
32
17
|
import type { SettingsConfig } from "../screens/types";
|
|
33
18
|
import type { FeedbackFormData } from "../utils/config-creators";
|
|
34
19
|
import type { AppInfo, FAQData, UserProfileDisplay, AdditionalScreen } from "../navigation/types";
|
|
@@ -80,54 +65,31 @@ export const useSettingsScreenConfig = (
|
|
|
80
65
|
} = features;
|
|
81
66
|
|
|
82
67
|
const { t } = useLocalization();
|
|
83
|
-
const { user, loading, isAuthReady
|
|
84
|
-
const { deleteAccount } = useAccountManagement();
|
|
68
|
+
const { user, loading, isAuthReady } = useAuth();
|
|
85
69
|
const userProfileData = useUserProfile({});
|
|
86
|
-
const { showAuthModal } = useAuthModalStore();
|
|
87
70
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
await Linking.openURL(url);
|
|
92
|
-
}
|
|
93
|
-
}, [appInfo.appStoreUrl]);
|
|
71
|
+
// Use centralized auth handlers
|
|
72
|
+
const { handleRatePress, handleSignOut, handleDeleteAccount, handleSignIn } =
|
|
73
|
+
useAuthHandlers(appInfo);
|
|
94
74
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}, [showAuthModal]);
|
|
114
|
-
|
|
115
|
-
const settingsConfig = useMemo((): SettingsConfig => ({
|
|
116
|
-
appearance: showAppearance ? createAppearanceConfig(t) : false,
|
|
117
|
-
language: showLanguage ? createLanguageConfig(t) : false,
|
|
118
|
-
notifications: showNotifications ? createNotificationsConfig(t) : false,
|
|
119
|
-
feedback: showFeedback ? createFeedbackConfig(t, onFeedbackSubmit) : false,
|
|
120
|
-
about: showAbout ? createAboutConfig(t) : false,
|
|
121
|
-
legal: showLegal ? createLegalConfig(t) : false,
|
|
122
|
-
faqs: showFaqs ? createFAQConfig(t) : false,
|
|
123
|
-
rating: showRating ? createRatingConfig(t, handleRatePress, appInfo.appStoreUrl || "") : false,
|
|
124
|
-
subscription: createSubscriptionConfig(t, isPremium, "SubscriptionDetail"),
|
|
125
|
-
disclaimer: false,
|
|
126
|
-
}), [
|
|
127
|
-
t, onFeedbackSubmit, handleRatePress, appInfo.appStoreUrl, isPremium,
|
|
128
|
-
showAppearance, showLanguage, showNotifications, showFeedback,
|
|
129
|
-
showAbout, showLegal, showFaqs, showRating,
|
|
130
|
-
]);
|
|
75
|
+
// Use settings config factory
|
|
76
|
+
const settingsConfig = useSettingsConfigFactory({
|
|
77
|
+
t,
|
|
78
|
+
onFeedbackSubmit,
|
|
79
|
+
handleRatePress,
|
|
80
|
+
appStoreUrl: appInfo.appStoreUrl || "",
|
|
81
|
+
isPremium,
|
|
82
|
+
features: {
|
|
83
|
+
notifications: showNotifications,
|
|
84
|
+
appearance: showAppearance,
|
|
85
|
+
language: showLanguage,
|
|
86
|
+
feedback: showFeedback,
|
|
87
|
+
rating: showRating,
|
|
88
|
+
faqs: showFaqs,
|
|
89
|
+
about: showAbout,
|
|
90
|
+
legal: showLegal,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
131
93
|
|
|
132
94
|
const userProfile = useMemo(() => createUserProfileDisplay({
|
|
133
95
|
profileData: userProfileData,
|
|
@@ -147,20 +109,11 @@ export const useSettingsScreenConfig = (
|
|
|
147
109
|
t,
|
|
148
110
|
}), [user, userProfileData, handleSignIn, handleSignOut, handleDeleteAccount, t]);
|
|
149
111
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
title: t(category.title),
|
|
156
|
-
items: category.items.map((item) => ({
|
|
157
|
-
id: item.id,
|
|
158
|
-
question: t(item.question, { appName: appInfo.name }),
|
|
159
|
-
answer: t(item.answer, { appName: appInfo.name }),
|
|
160
|
-
})),
|
|
161
|
-
})),
|
|
162
|
-
};
|
|
163
|
-
}, [faqData, t, appInfo.name]);
|
|
112
|
+
// Use centralized FAQ translation
|
|
113
|
+
const translatedFaqData = useMemo(() =>
|
|
114
|
+
translateFAQData(faqData, t, appInfo),
|
|
115
|
+
[faqData, t, appInfo]
|
|
116
|
+
);
|
|
164
117
|
|
|
165
118
|
return {
|
|
166
119
|
settingsConfig,
|
|
@@ -7,20 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
import React from "react";
|
|
9
9
|
import {
|
|
10
|
-
useAppDesignTokens,
|
|
11
10
|
StackNavigator,
|
|
12
|
-
type StackScreen,
|
|
13
11
|
type StackNavigatorConfig,
|
|
14
12
|
} from "@umituz/react-native-design-system";
|
|
15
|
-
import { useLocalization
|
|
16
|
-
import { NotificationSettingsScreen } from "../../domains/notifications";
|
|
17
|
-
import { AccountScreen } from "@umituz/react-native-auth";
|
|
18
|
-
import { SettingsScreen } from "../screens/SettingsScreen";
|
|
19
|
-
import { AppearanceScreen } from "../screens/AppearanceScreen";
|
|
20
|
-
import { FAQScreen } from "../../domains/faqs";
|
|
21
|
-
import { AboutScreen } from "../../domains/about";
|
|
22
|
-
import { LegalScreen } from "../../domains/legal";
|
|
23
|
-
import { GamificationScreen } from "../../domains/gamification";
|
|
13
|
+
import { useLocalization } from "../../domains/localization";
|
|
24
14
|
import { useNavigationHandlers, useSettingsScreens } from "./hooks";
|
|
25
15
|
import {
|
|
26
16
|
createNotificationTranslations,
|
|
@@ -33,20 +23,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = (pr
|
|
|
33
23
|
const {
|
|
34
24
|
appInfo,
|
|
35
25
|
legalUrls,
|
|
36
|
-
faqData,
|
|
37
|
-
config = {},
|
|
38
|
-
showUserProfile = false,
|
|
39
|
-
userProfile,
|
|
40
|
-
accountConfig,
|
|
41
|
-
additionalScreens = [],
|
|
42
|
-
devSettings,
|
|
43
|
-
customSections = [],
|
|
44
|
-
showHeader = true,
|
|
45
|
-
showCloseButton = false,
|
|
46
|
-
onClose,
|
|
47
|
-
gamificationConfig,
|
|
48
26
|
} = props;
|
|
49
|
-
const tokens = useAppDesignTokens();
|
|
50
27
|
const { t } = useLocalization();
|
|
51
28
|
const { handlePrivacyPress, handleTermsPress, handleEulaPress, aboutConfig } =
|
|
52
29
|
useNavigationHandlers(appInfo, legalUrls);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
2
|
import type { StackScreen } from "@umituz/react-native-design-system";
|
|
3
3
|
import { LanguageSelectionScreen } from "../../../domains/localization";
|
|
4
4
|
import { NotificationSettingsScreen } from "../../../domains/notifications";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Base Settings Config Creators
|
|
3
3
|
* Standard settings: appearance, language, notifications, about, legal
|
|
4
|
+
* Refactored to use configFactory pattern for consistency
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import type {
|
|
@@ -11,6 +12,7 @@ import type {
|
|
|
11
12
|
LegalConfig,
|
|
12
13
|
} from "../../screens/types";
|
|
13
14
|
import type { TranslationFunction } from "./types";
|
|
15
|
+
import { createBaseConfig, createConfigWithExtensions } from "../../../infrastructure/utils/configFactory";
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Create appearance configuration
|
|
@@ -18,14 +20,15 @@ import type { TranslationFunction } from "./types";
|
|
|
18
20
|
export const createAppearanceConfig = (
|
|
19
21
|
t: TranslationFunction,
|
|
20
22
|
routeOrOnPress?: string | (() => void),
|
|
21
|
-
): AppearanceConfig =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
});
|
|
23
|
+
): AppearanceConfig => {
|
|
24
|
+
return createBaseConfig<AppearanceConfig>({
|
|
25
|
+
t,
|
|
26
|
+
titleKey: "settings.appearance.title",
|
|
27
|
+
descriptionKey: "settings.appearance.description",
|
|
28
|
+
icon: "color-palette-outline",
|
|
29
|
+
routeOrOnPress,
|
|
30
|
+
});
|
|
31
|
+
};
|
|
29
32
|
|
|
30
33
|
/**
|
|
31
34
|
* Create language configuration
|
|
@@ -33,14 +36,15 @@ export const createAppearanceConfig = (
|
|
|
33
36
|
export const createLanguageConfig = (
|
|
34
37
|
t: TranslationFunction,
|
|
35
38
|
routeOrOnPress?: string | (() => void),
|
|
36
|
-
): LanguageConfig =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
39
|
+
): LanguageConfig => {
|
|
40
|
+
return createBaseConfig<LanguageConfig>({
|
|
41
|
+
t,
|
|
42
|
+
titleKey: "settings.language.title",
|
|
43
|
+
descriptionKey: "settings.language.description",
|
|
44
|
+
icon: "globe-outline",
|
|
45
|
+
routeOrOnPress,
|
|
46
|
+
});
|
|
47
|
+
};
|
|
44
48
|
|
|
45
49
|
/**
|
|
46
50
|
* Create notifications configuration
|
|
@@ -48,16 +52,22 @@ export const createLanguageConfig = (
|
|
|
48
52
|
export const createNotificationsConfig = (
|
|
49
53
|
t: TranslationFunction,
|
|
50
54
|
routeOrOnPress?: string | (() => void),
|
|
51
|
-
): NotificationsConfig =>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
55
|
+
): NotificationsConfig => {
|
|
56
|
+
return createConfigWithExtensions<NotificationsConfig>(
|
|
57
|
+
{
|
|
58
|
+
t,
|
|
59
|
+
titleKey: "settings.notifications.title",
|
|
60
|
+
descriptionKey: "settings.notifications.description",
|
|
61
|
+
icon: "notifications-outline",
|
|
62
|
+
routeOrOnPress,
|
|
63
|
+
defaultRoute: "Notifications",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
showToggle: false,
|
|
67
|
+
sectionTitle: t("settings.notifications.sectionTitle"),
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
};
|
|
61
71
|
|
|
62
72
|
/**
|
|
63
73
|
* Create about configuration
|
|
@@ -65,14 +75,15 @@ export const createNotificationsConfig = (
|
|
|
65
75
|
export const createAboutConfig = (
|
|
66
76
|
t: TranslationFunction,
|
|
67
77
|
routeOrOnPress?: string | (() => void),
|
|
68
|
-
): AboutConfig =>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
});
|
|
78
|
+
): AboutConfig => {
|
|
79
|
+
return createBaseConfig<AboutConfig>({
|
|
80
|
+
t,
|
|
81
|
+
titleKey: "settings.about.title",
|
|
82
|
+
descriptionKey: "settings.about.description",
|
|
83
|
+
icon: "information-circle-outline",
|
|
84
|
+
routeOrOnPress,
|
|
85
|
+
});
|
|
86
|
+
};
|
|
76
87
|
|
|
77
88
|
/**
|
|
78
89
|
* Create legal configuration
|
|
@@ -80,11 +91,12 @@ export const createAboutConfig = (
|
|
|
80
91
|
export const createLegalConfig = (
|
|
81
92
|
t: TranslationFunction,
|
|
82
93
|
routeOrOnPress?: string | (() => void),
|
|
83
|
-
): LegalConfig =>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
94
|
+
): LegalConfig => {
|
|
95
|
+
return createBaseConfig<LegalConfig>({
|
|
96
|
+
t,
|
|
97
|
+
titleKey: "settings.legal.title",
|
|
98
|
+
descriptionKey: "settings.legal.description",
|
|
99
|
+
icon: "document-text-outline",
|
|
100
|
+
routeOrOnPress,
|
|
101
|
+
});
|
|
102
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FAQ Translation Utility
|
|
3
|
+
* Handles translation of FAQ data
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TranslationFunction } from "./config-creators/types";
|
|
7
|
+
import type { FAQData } from "../navigation/types";
|
|
8
|
+
import type { AppInfo } from "../navigation/types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Translates FAQ data using the provided translation function
|
|
12
|
+
*/
|
|
13
|
+
export const translateFAQData = (
|
|
14
|
+
faqData: FAQData | undefined,
|
|
15
|
+
t: TranslationFunction,
|
|
16
|
+
_appInfo: AppInfo
|
|
17
|
+
): FAQData | undefined => {
|
|
18
|
+
if (!faqData) return undefined;
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
categories: faqData.categories.map((category) => ({
|
|
22
|
+
id: category.id,
|
|
23
|
+
title: t(category.title),
|
|
24
|
+
items: category.items.map((item) => ({
|
|
25
|
+
id: item.id,
|
|
26
|
+
question: t(item.question),
|
|
27
|
+
answer: t(item.answer),
|
|
28
|
+
})),
|
|
29
|
+
})),
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Presentation Utilities
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export * from "./config-creators";
|
|
6
|
+
export * from "./userProfileUtils";
|
|
7
|
+
export * from "./accountConfigUtils";
|
|
8
|
+
export * from "./faqTranslator";
|
|
9
|
+
export * from "./settingsConfigFactory";
|
|
10
|
+
export * from "./useAuthHandlers";
|
|
@@ -36,7 +36,7 @@ export function createStackScreen(
|
|
|
36
36
|
/**
|
|
37
37
|
* Create a screen with props
|
|
38
38
|
*/
|
|
39
|
-
export function createScreenWithProps<P
|
|
39
|
+
export function createScreenWithProps<P>(
|
|
40
40
|
name: string,
|
|
41
41
|
component: React.ComponentType<P>,
|
|
42
42
|
props: P,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Config Factory Utility
|
|
3
|
+
* Creates settings configuration based on feature flags
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
import type { TranslationFunction, FeedbackFormData } from "./config-creators/types";
|
|
8
|
+
import type { SettingsConfig } from "../screens/types";
|
|
9
|
+
import {
|
|
10
|
+
createAppearanceConfig,
|
|
11
|
+
createLanguageConfig,
|
|
12
|
+
createNotificationsConfig,
|
|
13
|
+
createAboutConfig,
|
|
14
|
+
createLegalConfig,
|
|
15
|
+
createFeedbackConfig,
|
|
16
|
+
createRatingConfig,
|
|
17
|
+
createFAQConfig,
|
|
18
|
+
createSubscriptionConfig,
|
|
19
|
+
} from "./config-creators";
|
|
20
|
+
|
|
21
|
+
export interface SettingsConfigFactoryParams {
|
|
22
|
+
t: TranslationFunction;
|
|
23
|
+
onFeedbackSubmit: (data: FeedbackFormData) => Promise<void>;
|
|
24
|
+
handleRatePress: () => void;
|
|
25
|
+
appStoreUrl: string;
|
|
26
|
+
isPremium: boolean;
|
|
27
|
+
features: {
|
|
28
|
+
notifications: boolean;
|
|
29
|
+
appearance: boolean;
|
|
30
|
+
language: boolean;
|
|
31
|
+
feedback: boolean;
|
|
32
|
+
rating: boolean;
|
|
33
|
+
faqs: boolean;
|
|
34
|
+
about: boolean;
|
|
35
|
+
legal: boolean;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates settings configuration based on enabled features
|
|
41
|
+
*/
|
|
42
|
+
export const createSettingsConfig = (
|
|
43
|
+
params: SettingsConfigFactoryParams
|
|
44
|
+
): SettingsConfig => {
|
|
45
|
+
const {
|
|
46
|
+
t,
|
|
47
|
+
onFeedbackSubmit,
|
|
48
|
+
handleRatePress,
|
|
49
|
+
appStoreUrl,
|
|
50
|
+
isPremium,
|
|
51
|
+
features,
|
|
52
|
+
} = params;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
appearance: features.appearance ? createAppearanceConfig(t) : false,
|
|
56
|
+
language: features.language ? createLanguageConfig(t) : false,
|
|
57
|
+
notifications: features.notifications ? createNotificationsConfig(t) : false,
|
|
58
|
+
feedback: features.feedback ? createFeedbackConfig(t, onFeedbackSubmit) : false,
|
|
59
|
+
about: features.about ? createAboutConfig(t) : false,
|
|
60
|
+
legal: features.legal ? createLegalConfig(t) : false,
|
|
61
|
+
faqs: features.faqs ? createFAQConfig(t) : false,
|
|
62
|
+
rating: features.rating ? createRatingConfig(t, handleRatePress, appStoreUrl) : false,
|
|
63
|
+
subscription: createSubscriptionConfig(t, isPremium, "SubscriptionDetail"),
|
|
64
|
+
disclaimer: false,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Hook for creating settings config with memoization
|
|
70
|
+
*/
|
|
71
|
+
export const useSettingsConfigFactory = (
|
|
72
|
+
params: SettingsConfigFactoryParams
|
|
73
|
+
): SettingsConfig => {
|
|
74
|
+
return useMemo(() => createSettingsConfig(params), [
|
|
75
|
+
params.t,
|
|
76
|
+
params.onFeedbackSubmit,
|
|
77
|
+
params.handleRatePress,
|
|
78
|
+
params.appStoreUrl,
|
|
79
|
+
params.isPremium,
|
|
80
|
+
params.features.appearance,
|
|
81
|
+
params.features.language,
|
|
82
|
+
params.features.notifications,
|
|
83
|
+
params.features.feedback,
|
|
84
|
+
params.features.about,
|
|
85
|
+
params.features.legal,
|
|
86
|
+
params.features.faqs,
|
|
87
|
+
params.features.rating,
|
|
88
|
+
]);
|
|
89
|
+
};
|