@umituz/react-native-settings 4.23.86 → 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 +1 -1
- package/src/domains/feedback/presentation/components/FeedbackForm.tsx +10 -3
- 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/reminders/presentation/components/ReminderForm.tsx +51 -45
- 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/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/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/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,
|
|
@@ -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";
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Handlers Hook
|
|
3
|
+
* Centralized authentication-related handlers for settings screen
|
|
4
|
+
* FIXED: Added proper error logging and user feedback
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useCallback } from "react";
|
|
8
|
+
import { Linking, Alert } from "react-native";
|
|
9
|
+
import {
|
|
10
|
+
useAuth,
|
|
11
|
+
useAccountManagement,
|
|
12
|
+
useAuthModalStore,
|
|
13
|
+
} from "@umituz/react-native-auth";
|
|
14
|
+
import { AlertService } from "@umituz/react-native-design-system";
|
|
15
|
+
import { useLocalization } from "../../domains/localization";
|
|
16
|
+
import type { AppInfo } from "../navigation/types";
|
|
17
|
+
|
|
18
|
+
declare const __DEV__: boolean;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Hook that provides authentication-related handlers
|
|
22
|
+
*/
|
|
23
|
+
export const useAuthHandlers = (appInfo: AppInfo) => {
|
|
24
|
+
const { t } = useLocalization();
|
|
25
|
+
const { signOut } = useAuth();
|
|
26
|
+
const { deleteAccount } = useAccountManagement();
|
|
27
|
+
const { showAuthModal } = useAuthModalStore();
|
|
28
|
+
|
|
29
|
+
const handleRatePress = useCallback(async () => {
|
|
30
|
+
const url = appInfo.appStoreUrl;
|
|
31
|
+
if (!url) {
|
|
32
|
+
// FIXED: Provide feedback when URL is missing
|
|
33
|
+
Alert.alert(t("common.error"), "App store URL not configured");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const canOpen = await Linking.canOpenURL(url);
|
|
39
|
+
if (!canOpen) {
|
|
40
|
+
// FIXED: Provide feedback when URL cannot be opened
|
|
41
|
+
Alert.alert(
|
|
42
|
+
t("common.error"),
|
|
43
|
+
"Unable to open app store. Please check your device settings."
|
|
44
|
+
);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
await Linking.openURL(url);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// FIXED: Log actual error for debugging
|
|
50
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
51
|
+
console.error("[useAuthHandlers] Failed to open app store:", error);
|
|
52
|
+
}
|
|
53
|
+
Alert.alert(t("common.error"), "Failed to open app store");
|
|
54
|
+
}
|
|
55
|
+
}, [appInfo.appStoreUrl, t]);
|
|
56
|
+
|
|
57
|
+
const handleSignOut = useCallback(async () => {
|
|
58
|
+
try {
|
|
59
|
+
await signOut();
|
|
60
|
+
} catch (error) {
|
|
61
|
+
// FIXED: Log actual error for debugging
|
|
62
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
63
|
+
console.error("[useAuthHandlers] Sign out failed:", error);
|
|
64
|
+
}
|
|
65
|
+
AlertService.createErrorAlert(
|
|
66
|
+
t("common.error"),
|
|
67
|
+
t("auth.errors.unknownError")
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}, [signOut, t]);
|
|
71
|
+
|
|
72
|
+
const handleDeleteAccount = useCallback(async () => {
|
|
73
|
+
try {
|
|
74
|
+
await deleteAccount();
|
|
75
|
+
} catch (error) {
|
|
76
|
+
// FIXED: Log actual error for debugging
|
|
77
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
78
|
+
console.error("[useAuthHandlers] Delete account failed:", error);
|
|
79
|
+
}
|
|
80
|
+
AlertService.createErrorAlert(
|
|
81
|
+
t("common.error"),
|
|
82
|
+
t("settings.account.deleteError")
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}, [deleteAccount, t]);
|
|
86
|
+
|
|
87
|
+
const handleSignIn = useCallback(() => {
|
|
88
|
+
// FIXED: Remove empty callback - pass undefined instead
|
|
89
|
+
showAuthModal(undefined, "login");
|
|
90
|
+
}, [showAuthModal]);
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
handleRatePress,
|
|
94
|
+
handleSignOut,
|
|
95
|
+
handleDeleteAccount,
|
|
96
|
+
handleSignIn,
|
|
97
|
+
};
|
|
98
|
+
};
|