@umituz/react-native-settings 5.2.34 → 5.2.36
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 +2 -4
- package/src/domains/about/presentation/screens/AboutScreenContent.tsx +87 -63
- package/src/domains/appearance/data/colorPalettes.ts +0 -23
- package/src/domains/appearance/presentation/components/CustomColorsSection.tsx +2 -4
- package/src/domains/appearance/presentation/components/ThemeOption.tsx +2 -2
- package/src/domains/dev/presentation/components/DevSettingsSection.tsx +5 -2
- package/src/domains/faqs/presentation/screens/FAQScreen.tsx +19 -25
- package/src/domains/feedback/presentation/components/FeedbackForm.tsx +160 -81
- package/src/domains/gamification/components/GamificationScreen/GamificationScreenWithConfig.tsx +11 -11
- package/src/domains/localization/infrastructure/components/LanguageSwitcher.tsx +0 -2
- package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +1 -1
- package/src/domains/localization/presentation/screens/LanguageSelectionScreen.tsx +85 -48
- package/src/domains/localization/presentation/screens/__tests__/LanguageSelectionScreen.test.tsx +0 -15
- package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +1 -3
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +0 -4
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +69 -31
- package/src/domains/rating/presentation/components/StarRating.tsx +7 -13
- package/src/infrastructure/utils/configFactory.ts +0 -26
- package/src/infrastructure/utils/constants/textLimits.ts +0 -2
- package/src/infrastructure/utils/sanitizers.ts +1 -25
- package/src/infrastructure/utils/validation/core.ts +0 -33
- package/src/infrastructure/utils/validation/formValidators.ts +7 -1
- package/src/infrastructure/utils/validation/index.ts +2 -33
- package/src/infrastructure/utils/validators.ts +0 -6
- package/src/presentation/navigation/utils/index.ts +1 -7
- package/src/presentation/navigation/utils/navigationHelpers.ts +2 -87
- package/src/presentation/screens/components/SettingsContent.tsx +4 -19
- package/src/presentation/screens/components/sections/CustomSettingsList.tsx +3 -8
- package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +0 -4
- package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +0 -4
- package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +0 -4
- package/src/presentation/utils/screenFactory.ts +0 -25
- package/src/utils/appUtils.ts +0 -18
- package/src/utils/devUtils.ts +0 -10
- package/src/utils/errorUtils.ts +0 -22
- package/src/domains/about/utils/index.ts +0 -156
- package/src/domains/faqs/domain/services/index.ts +0 -1
- package/src/domains/faqs/presentation/screens/index.ts +0 -2
- package/src/domains/gamification/components/GamificationScreen/Header.tsx +0 -30
- package/src/domains/legal/presentation/components/LegalLinks.tsx +0 -137
- package/src/domains/legal/presentation/components/index.ts +0 -5
- package/src/domains/localization/infrastructure/config/languagesData.ts +0 -26
- package/src/domains/localization/infrastructure/hooks/TranslationHook.ts +0 -37
- package/src/domains/localization/infrastructure/storage/AsyncStorageWrapper.ts +0 -34
- package/src/infrastructure/storage/storeConfig.ts +0 -114
- package/src/infrastructure/types/commonComponentTypes.ts +0 -142
- package/src/infrastructure/utils/async/core.ts +0 -110
- package/src/infrastructure/utils/async/debounceAndBatch.ts +0 -69
- package/src/infrastructure/utils/async/index.ts +0 -8
- package/src/infrastructure/utils/async/retryAndTimeout.ts +0 -65
- package/src/infrastructure/utils/dateUtils.ts +0 -61
- package/src/infrastructure/utils/errorHandlers.ts +0 -250
- package/src/infrastructure/utils/index.ts +0 -12
- package/src/infrastructure/utils/memoComparisonUtils.ts +0 -66
- package/src/infrastructure/utils/memoUtils.ts +0 -167
- package/src/infrastructure/utils/styleTokens.ts +0 -145
- package/src/infrastructure/utils/styles/componentStyles.ts +0 -90
- package/src/infrastructure/utils/styles/index.ts +0 -9
- package/src/infrastructure/utils/styles/layoutStyles.ts +0 -56
- package/src/infrastructure/utils/styles/spacingStyles.ts +0 -33
- package/src/infrastructure/utils/styles/styleHelpers.ts +0 -22
- package/src/infrastructure/utils/translationHelpers.ts +0 -81
- package/src/infrastructure/utils/validation/numericValidators.ts +0 -66
- package/src/infrastructure/utils/validation/passwordValidator.ts +0 -53
- package/src/infrastructure/utils/validation/textValidators.ts +0 -118
- package/src/presentation/components/ErrorBoundary/SettingsErrorBoundary.tsx +0 -105
- package/src/presentation/components/ErrorBoundary/index.ts +0 -12
- package/src/presentation/components/ErrorBoundary/withErrorBoundary.tsx +0 -45
- package/src/utils/hooks/index.ts +0 -6
- package/src/utils/index.ts +0 -3
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Navigation Utilities
|
|
3
3
|
*/
|
|
4
|
-
export {
|
|
5
|
-
createNavigationHandler,
|
|
6
|
-
createRouteOrPressHandler,
|
|
7
|
-
createNavigationHandlerWithParams,
|
|
8
|
-
} from './navigationHelpers';
|
|
9
|
-
export type { NavigateFunction, RouteOrPressConfig } from './navigationHelpers';
|
|
10
|
-
|
|
4
|
+
export { createRouteOrPressHandler } from './navigationHelpers';
|
|
11
5
|
export { createNotificationTranslations, createQuietHoursTranslations, createLegalScreenProps } from './navigationTranslations';
|
|
@@ -5,74 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
import type { SettingsStackParamList } from '../types';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
* Type for navigation function
|
|
10
|
-
*/
|
|
11
|
-
export type NavigateFunction = <RouteName extends keyof SettingsStackParamList>(
|
|
8
|
+
type NavigateFunction = <RouteName extends keyof SettingsStackParamList>(
|
|
12
9
|
route: RouteName,
|
|
13
10
|
params?: SettingsStackParamList[RouteName]
|
|
14
11
|
) => void;
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
* Create a type-safe navigation handler
|
|
18
|
-
*
|
|
19
|
-
* @param navigate - Navigation function from useNavigation
|
|
20
|
-
* @param route - Target route name
|
|
21
|
-
* @param fallback - Optional fallback route if primary route is undefined
|
|
22
|
-
* @returns Navigation handler function
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```typescript
|
|
26
|
-
* const navigation = useSettingsNavigation();
|
|
27
|
-
* const handlePress = createNavigationHandler(
|
|
28
|
-
* navigation.navigate,
|
|
29
|
-
* config.route,
|
|
30
|
-
* 'Appearance'
|
|
31
|
-
* );
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
export const createNavigationHandler = <T extends keyof SettingsStackParamList>(
|
|
35
|
-
navigate: NavigateFunction,
|
|
36
|
-
route: T | undefined,
|
|
37
|
-
fallback?: T
|
|
38
|
-
) => {
|
|
39
|
-
return () => {
|
|
40
|
-
const targetRoute = route || fallback;
|
|
41
|
-
if (targetRoute) {
|
|
42
|
-
navigate(targetRoute);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Configuration for route or press handler
|
|
49
|
-
*/
|
|
50
|
-
export interface RouteOrPressConfig<T extends keyof SettingsStackParamList = keyof SettingsStackParamList> {
|
|
51
|
-
/** Route to navigate to */
|
|
13
|
+
interface RouteOrPressConfig<T extends keyof SettingsStackParamList = keyof SettingsStackParamList> {
|
|
52
14
|
route?: T;
|
|
53
|
-
/** Custom onPress handler */
|
|
54
15
|
onPress?: () => void;
|
|
55
|
-
/** Fallback route if primary route is undefined */
|
|
56
16
|
fallback?: T;
|
|
57
17
|
}
|
|
58
18
|
|
|
59
|
-
/**
|
|
60
|
-
* Create a handler that supports both route navigation and custom onPress
|
|
61
|
-
* Prioritizes onPress if provided, otherwise navigates to route
|
|
62
|
-
*
|
|
63
|
-
* @param navigate - Navigation function from useNavigation
|
|
64
|
-
* @param config - Configuration with route and/or onPress
|
|
65
|
-
* @returns Handler function
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```typescript
|
|
69
|
-
* const navigation = useSettingsNavigation();
|
|
70
|
-
* const handlePress = createRouteOrPressHandler(
|
|
71
|
-
* navigation.navigate,
|
|
72
|
-
* { route: config.route, onPress: config.onPress, fallback: 'Appearance' }
|
|
73
|
-
* );
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
19
|
export const createRouteOrPressHandler = <T extends keyof SettingsStackParamList>(
|
|
77
20
|
navigate: NavigateFunction,
|
|
78
21
|
config: RouteOrPressConfig<T>
|
|
@@ -88,31 +31,3 @@ export const createRouteOrPressHandler = <T extends keyof SettingsStackParamList
|
|
|
88
31
|
}
|
|
89
32
|
};
|
|
90
33
|
};
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Create a navigation handler with parameters
|
|
94
|
-
*
|
|
95
|
-
* @param navigate - Navigation function from useNavigation
|
|
96
|
-
* @param route - Target route name
|
|
97
|
-
* @param params - Navigation parameters for the route
|
|
98
|
-
* @returns Navigation handler function
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* ```typescript
|
|
102
|
-
* const navigation = useSettingsNavigation();
|
|
103
|
-
* const handlePress = createNavigationHandlerWithParams(
|
|
104
|
-
* navigation.navigate,
|
|
105
|
-
* 'LegalDetail',
|
|
106
|
-
* { document: 'privacy' }
|
|
107
|
-
* );
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
|
-
export const createNavigationHandlerWithParams = <T extends keyof SettingsStackParamList>(
|
|
111
|
-
navigate: NavigateFunction,
|
|
112
|
-
route: T,
|
|
113
|
-
params: SettingsStackParamList[T]
|
|
114
|
-
) => {
|
|
115
|
-
return () => {
|
|
116
|
-
navigate(route, params);
|
|
117
|
-
};
|
|
118
|
-
};
|
|
@@ -13,6 +13,9 @@ import { CustomSettingsList } from "./sections/CustomSettingsList";
|
|
|
13
13
|
import { hasAnyFeaturesEnabled } from "./utils/featureChecker";
|
|
14
14
|
import { useGamification } from "../../../domains/gamification";
|
|
15
15
|
import type { SettingsContentProps } from "./types/SettingsContentProps";
|
|
16
|
+
import type { CustomSettingsSection } from "../types";
|
|
17
|
+
|
|
18
|
+
const EMPTY_CUSTOM_SECTIONS: CustomSettingsSection[] = [];
|
|
16
19
|
|
|
17
20
|
export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
18
21
|
normalizedConfig,
|
|
@@ -22,7 +25,7 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
|
22
25
|
showFooter = true,
|
|
23
26
|
footerText,
|
|
24
27
|
appVersion,
|
|
25
|
-
customSections =
|
|
28
|
+
customSections = EMPTY_CUSTOM_SECTIONS,
|
|
26
29
|
emptyStateText,
|
|
27
30
|
devSettings,
|
|
28
31
|
gamificationConfig,
|
|
@@ -111,24 +114,6 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
|
111
114
|
);
|
|
112
115
|
};
|
|
113
116
|
|
|
114
|
-
export const MemoizedSettingsContent = React.memo(SettingsContent, (prevProps, nextProps) => {
|
|
115
|
-
return (
|
|
116
|
-
prevProps.normalizedConfig === nextProps.normalizedConfig &&
|
|
117
|
-
prevProps.features === nextProps.features &&
|
|
118
|
-
prevProps.showUserProfile === nextProps.showUserProfile &&
|
|
119
|
-
prevProps.userProfile === nextProps.userProfile &&
|
|
120
|
-
prevProps.showFooter === nextProps.showFooter &&
|
|
121
|
-
prevProps.footerText === nextProps.footerText &&
|
|
122
|
-
prevProps.appVersion === nextProps.appVersion &&
|
|
123
|
-
prevProps.customSections === nextProps.customSections &&
|
|
124
|
-
prevProps.emptyStateText === nextProps.emptyStateText &&
|
|
125
|
-
prevProps.devSettings === nextProps.devSettings &&
|
|
126
|
-
prevProps.gamificationConfig === nextProps.gamificationConfig
|
|
127
|
-
);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
MemoizedSettingsContent.displayName = "MemoizedSettingsContent";
|
|
131
|
-
|
|
132
117
|
const styles = StyleSheet.create({
|
|
133
118
|
container: {
|
|
134
119
|
flex: 1,
|
|
@@ -2,13 +2,14 @@ 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";
|
|
6
5
|
|
|
7
6
|
interface CustomSettingsListProps {
|
|
8
7
|
customSections?: CustomSettingsSection[];
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
const EMPTY_SECTIONS: CustomSettingsSection[] = [];
|
|
11
|
+
|
|
12
|
+
export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({ customSections = EMPTY_SECTIONS }) => {
|
|
12
13
|
const sortedSections = useMemo(() => {
|
|
13
14
|
return Array.from(customSections)
|
|
14
15
|
.sort((a: CustomSettingsSection, b: CustomSettingsSection) => (a.order ?? 999) - (b.order ?? 999));
|
|
@@ -43,9 +44,3 @@ export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({ customSe
|
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
CustomSettingsList.displayName = "CustomSettingsList";
|
|
46
|
-
|
|
47
|
-
export const MemoizedCustomSettingsList = React.memo(
|
|
48
|
-
CustomSettingsList,
|
|
49
|
-
createSinglePropComparator("customSections")
|
|
50
|
-
);
|
|
51
|
-
MemoizedCustomSettingsList.displayName = "MemoizedCustomSettingsList";
|
|
@@ -5,7 +5,6 @@ import { getLanguageByCode } from "../../../../domains/localization";
|
|
|
5
5
|
import { SettingsItemCard } from "../../../components/SettingsItemCard";
|
|
6
6
|
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|
|
7
7
|
import { SettingsSection } from "../../../components/SettingsSection";
|
|
8
|
-
import { compareConfigAndFeatures } from "../../../../infrastructure/utils/memoComparisonUtils";
|
|
9
8
|
import { useSettingsNavigation } from "../../../navigation/hooks/useSettingsNavigation";
|
|
10
9
|
|
|
11
10
|
interface FeatureSettingsSectionProps {
|
|
@@ -87,6 +86,3 @@ export const FeatureSettingsSection: React.FC<FeatureSettingsSectionProps> = ({
|
|
|
87
86
|
</SettingsSection>
|
|
88
87
|
);
|
|
89
88
|
};
|
|
90
|
-
|
|
91
|
-
export const MemoizedFeatureSettingsSection = React.memo(FeatureSettingsSection, compareConfigAndFeatures);
|
|
92
|
-
MemoizedFeatureSettingsSection.displayName = "MemoizedFeatureSettingsSection";
|
|
@@ -3,7 +3,6 @@ import { AboutSection } from "../../../../domains/about/presentation/components/
|
|
|
3
3
|
import { LegalSection } from "../../../../domains/legal/presentation/components/LegalSection";
|
|
4
4
|
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|
|
5
5
|
import { SettingsSection } from "../../../components/SettingsSection";
|
|
6
|
-
import { compareConfigAndFeatures } from "../../../../infrastructure/utils/memoComparisonUtils";
|
|
7
6
|
|
|
8
7
|
interface IdentitySettingsSectionProps {
|
|
9
8
|
normalizedConfig: NormalizedConfig;
|
|
@@ -48,6 +47,3 @@ export const IdentitySettingsSection: React.FC<IdentitySettingsSectionProps> = (
|
|
|
48
47
|
};
|
|
49
48
|
|
|
50
49
|
IdentitySettingsSection.displayName = "IdentitySettingsSection";
|
|
51
|
-
|
|
52
|
-
export const MemoizedIdentitySettingsSection = React.memo(IdentitySettingsSection, compareConfigAndFeatures);
|
|
53
|
-
MemoizedIdentitySettingsSection.displayName = "MemoizedIdentitySettingsSection";
|
|
@@ -4,7 +4,6 @@ import { SettingsSection } from "../../../components/SettingsSection";
|
|
|
4
4
|
import { SettingsItemCard } from "../../../components/SettingsItemCard";
|
|
5
5
|
import { SettingsNavigationItem } from "../../../components/SettingsNavigationItem";
|
|
6
6
|
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|
|
7
|
-
import { compareConfigAndFeatures } from "../../../../infrastructure/utils/memoComparisonUtils";
|
|
8
7
|
import { useSettingsNavigation } from "../../../navigation/hooks/useSettingsNavigation";
|
|
9
8
|
|
|
10
9
|
interface SupportSettingsSectionProps {
|
|
@@ -110,6 +109,3 @@ export const SupportSettingsSection: React.FC<SupportSettingsSectionProps> = ({
|
|
|
110
109
|
};
|
|
111
110
|
|
|
112
111
|
SupportSettingsSection.displayName = "SupportSettingsSection";
|
|
113
|
-
|
|
114
|
-
export const MemoizedSupportSettingsSection = React.memo(SupportSettingsSection, compareConfigAndFeatures);
|
|
115
|
-
MemoizedSupportSettingsSection.displayName = "MemoizedSupportSettingsSection";
|
|
@@ -8,31 +8,6 @@ import React from "react";
|
|
|
8
8
|
import type { StackScreen } from "@umituz/react-native-design-system";
|
|
9
9
|
import type { AdditionalScreen } from "../navigation/types";
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* Create a basic stack screen configuration
|
|
13
|
-
*/
|
|
14
|
-
export function createStackScreen<P = unknown>(
|
|
15
|
-
name: string,
|
|
16
|
-
componentOrChildren: React.ComponentType<P> | (() => 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 React.ComponentType<P>,
|
|
32
|
-
options: { headerShown: false, ...options },
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
11
|
/**
|
|
37
12
|
* Create a screen with props
|
|
38
13
|
*/
|
package/src/utils/appUtils.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* App and Platform Utilities
|
|
3
3
|
*/
|
|
4
|
-
import { Platform } from "react-native";
|
|
5
4
|
import Constants from "expo-constants";
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -16,22 +15,5 @@ export function getAppVersion(): string {
|
|
|
16
15
|
return version;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
/**
|
|
20
|
-
* Gets the current build number from Expo constants
|
|
21
|
-
*/
|
|
22
|
-
export function getBuildNumber(): string | undefined {
|
|
23
|
-
return Constants.expoConfig?.ios?.buildNumber ?? Constants.expoConfig?.android?.versionCode?.toString();
|
|
24
|
-
}
|
|
25
18
|
|
|
26
|
-
/**
|
|
27
|
-
* Validates if the current platform is supported
|
|
28
|
-
*/
|
|
29
|
-
export function validatePlatform(): "ios" | "android" {
|
|
30
|
-
const platform = Platform.OS;
|
|
31
|
-
if (platform !== "ios" && platform !== "android") {
|
|
32
|
-
// Default to android for consistency if something goes wrong in detection
|
|
33
|
-
return "android";
|
|
34
|
-
}
|
|
35
|
-
return platform;
|
|
36
|
-
}
|
|
37
19
|
|
package/src/utils/devUtils.ts
CHANGED
|
@@ -48,13 +48,3 @@ export const devError = (...args: unknown[]): void => {
|
|
|
48
48
|
}
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
* Execute callback only in development mode
|
|
53
|
-
*
|
|
54
|
-
* @param callback - Callback to execute in dev mode
|
|
55
|
-
*/
|
|
56
|
-
export const devOnly = (callback: () => void): void => {
|
|
57
|
-
if (isDev()) {
|
|
58
|
-
callback();
|
|
59
|
-
}
|
|
60
|
-
};
|
package/src/utils/errorUtils.ts
CHANGED
|
@@ -30,25 +30,3 @@ export const formatErrorMessage = (error: unknown): string => {
|
|
|
30
30
|
return 'Unknown error';
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
* Format error for logging with optional context
|
|
35
|
-
* Useful for debugging and error tracking
|
|
36
|
-
*
|
|
37
|
-
* @param error - The error to format
|
|
38
|
-
* @param context - Optional context string (e.g., function name, operation)
|
|
39
|
-
* @returns Formatted error string with context
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```typescript
|
|
43
|
-
* try {
|
|
44
|
-
* await fetchUserData();
|
|
45
|
-
* } catch (err) {
|
|
46
|
-
* console.error(formatErrorForLogging(err, 'fetchUserData'));
|
|
47
|
-
* // Output: "[fetchUserData] Network timeout"
|
|
48
|
-
* }
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
export const formatErrorForLogging = (error: unknown, context?: string): string => {
|
|
52
|
-
const message = formatErrorMessage(error);
|
|
53
|
-
return context ? `[${context}] ${message}` : message;
|
|
54
|
-
};
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for About package
|
|
3
|
-
* General purpose utilities for all applications
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export { createDefaultAppInfo } from './AppInfoFactory';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Create default configuration with overrides
|
|
10
|
-
*/
|
|
11
|
-
export const createDefaultConfig = (overrides: Record<string, unknown> = {}) => {
|
|
12
|
-
if (!overrides || typeof overrides !== 'object') {
|
|
13
|
-
overrides = {};
|
|
14
|
-
}
|
|
15
|
-
const overridesObj = overrides as Record<string, Record<string, unknown>>;
|
|
16
|
-
return {
|
|
17
|
-
appInfo: {
|
|
18
|
-
name: '',
|
|
19
|
-
version: '1.0.0',
|
|
20
|
-
description: '',
|
|
21
|
-
developer: '',
|
|
22
|
-
contactEmail: '',
|
|
23
|
-
websiteUrl: '',
|
|
24
|
-
websiteDisplay: '',
|
|
25
|
-
moreAppsUrl: '',
|
|
26
|
-
privacyPolicyUrl: '',
|
|
27
|
-
termsOfServiceUrl: '',
|
|
28
|
-
...(overridesObj.appInfo || {}),
|
|
29
|
-
},
|
|
30
|
-
theme: {
|
|
31
|
-
primary: '#007AFF',
|
|
32
|
-
secondary: '#5856D6',
|
|
33
|
-
background: '#FFFFFF',
|
|
34
|
-
text: '#000000',
|
|
35
|
-
border: '#E5E5E5',
|
|
36
|
-
...(overridesObj.theme || {}),
|
|
37
|
-
},
|
|
38
|
-
style: {
|
|
39
|
-
containerStyle: {},
|
|
40
|
-
itemStyle: {},
|
|
41
|
-
textStyle: {},
|
|
42
|
-
iconStyle: {},
|
|
43
|
-
...(overridesObj.style || {}),
|
|
44
|
-
},
|
|
45
|
-
actions: {
|
|
46
|
-
onWebsitePress: undefined,
|
|
47
|
-
onEmailPress: undefined,
|
|
48
|
-
onPrivacyPress: undefined,
|
|
49
|
-
onTermsPress: undefined,
|
|
50
|
-
onMoreAppsPress: undefined,
|
|
51
|
-
...(overridesObj.actions || {}),
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Validate configuration object
|
|
58
|
-
*/
|
|
59
|
-
export const validateConfig = (config: unknown): boolean => {
|
|
60
|
-
if (!config || typeof config !== 'object') {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const configObj = config as Record<string, unknown>;
|
|
65
|
-
if (!configObj.appInfo || typeof configObj.appInfo !== 'object') {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return true;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Merge multiple configurations
|
|
74
|
-
*/
|
|
75
|
-
export const mergeConfigs = (...configs: Record<string, unknown>[]) => {
|
|
76
|
-
const result: Record<string, unknown> = {};
|
|
77
|
-
|
|
78
|
-
for (const config of configs.filter(Boolean)) {
|
|
79
|
-
if (config && typeof config === 'object') {
|
|
80
|
-
for (const [key, value] of Object.entries(config)) {
|
|
81
|
-
if (value && typeof value === 'object' && !Array.isArray(value) && result[key] && typeof result[key] === 'object' && !Array.isArray(result[key])) {
|
|
82
|
-
// Deep merge for nested objects
|
|
83
|
-
result[key] = { ...result[key] as Record<string, unknown>, ...value as Record<string, unknown> };
|
|
84
|
-
} else {
|
|
85
|
-
result[key] = value;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return result;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Validate email format
|
|
96
|
-
*/
|
|
97
|
-
export const isValidEmail = (email: string): boolean => {
|
|
98
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
99
|
-
// Additional check to prevent consecutive dots
|
|
100
|
-
return emailRegex.test(email) && !email.includes('..');
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Validate URL format
|
|
105
|
-
*/
|
|
106
|
-
export const isValidUrl = (url: string): boolean => {
|
|
107
|
-
try {
|
|
108
|
-
const urlObj = new URL(url);
|
|
109
|
-
// Only allow http and https protocols
|
|
110
|
-
return urlObj.protocol === 'http:' || urlObj.protocol === 'https:';
|
|
111
|
-
} catch {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Open URL in external browser
|
|
118
|
-
*/
|
|
119
|
-
export const openUrl = async (url: string): Promise<boolean> => {
|
|
120
|
-
try {
|
|
121
|
-
const { Linking } = await import('react-native');
|
|
122
|
-
const canOpen = await Linking.canOpenURL(url);
|
|
123
|
-
|
|
124
|
-
if (canOpen) {
|
|
125
|
-
await Linking.openURL(url);
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return false;
|
|
130
|
-
} catch {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Send email
|
|
137
|
-
*/
|
|
138
|
-
export const sendEmail = async (email: string, subject?: string): Promise<boolean> => {
|
|
139
|
-
try {
|
|
140
|
-
const { Linking } = await import('react-native');
|
|
141
|
-
const url = subject
|
|
142
|
-
? `mailto:${email}?subject=${encodeURIComponent(subject)}`
|
|
143
|
-
: `mailto:${email}`;
|
|
144
|
-
|
|
145
|
-
const canOpen = await Linking.canOpenURL(url);
|
|
146
|
-
|
|
147
|
-
if (canOpen) {
|
|
148
|
-
await Linking.openURL(url);
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return false;
|
|
153
|
-
} catch {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { FAQSearchService } from "./FAQSearchService";
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GamificationScreen Header Component
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import React from "react";
|
|
6
|
-
import { View, type ViewStyle, type TextStyle } from "react-native";
|
|
7
|
-
import { AtomicText } from "@umituz/react-native-design-system";
|
|
8
|
-
import { styles } from "./styles";
|
|
9
|
-
|
|
10
|
-
export interface HeaderProps {
|
|
11
|
-
title: string;
|
|
12
|
-
headerStyle?: ViewStyle;
|
|
13
|
-
titleStyle?: TextStyle;
|
|
14
|
-
textColor: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const Header: React.FC<HeaderProps> = ({
|
|
18
|
-
title,
|
|
19
|
-
headerStyle,
|
|
20
|
-
titleStyle,
|
|
21
|
-
textColor,
|
|
22
|
-
}) => {
|
|
23
|
-
return (
|
|
24
|
-
<View style={[styles.header, headerStyle]}>
|
|
25
|
-
<AtomicText style={[styles.title, { color: textColor }, titleStyle]}>
|
|
26
|
-
{title}
|
|
27
|
-
</AtomicText>
|
|
28
|
-
</View>
|
|
29
|
-
);
|
|
30
|
-
};
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LegalLinks Component
|
|
3
|
-
* Single Responsibility: Display Privacy Policy and Terms of Service links
|
|
4
|
-
* Required for App Store compliance in paywall screens
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React from "react";
|
|
8
|
-
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
9
|
-
import { AtomicText } from "@umituz/react-native-design-system";
|
|
10
|
-
import { UrlHandlerService } from "../../domain/services/UrlHandlerService";
|
|
11
|
-
|
|
12
|
-
export interface LegalLinksProps {
|
|
13
|
-
/**
|
|
14
|
-
* Privacy Policy URL
|
|
15
|
-
*/
|
|
16
|
-
privacyPolicyUrl?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Terms of Service URL
|
|
19
|
-
*/
|
|
20
|
-
termsOfServiceUrl?: string;
|
|
21
|
-
/**
|
|
22
|
-
* Privacy Policy link text (required when privacyPolicyUrl is provided)
|
|
23
|
-
*/
|
|
24
|
-
privacyText?: string;
|
|
25
|
-
/**
|
|
26
|
-
* Terms of Service link text (required when termsOfServiceUrl is provided)
|
|
27
|
-
*/
|
|
28
|
-
termsText?: string;
|
|
29
|
-
/**
|
|
30
|
-
* Callback when Privacy Policy is pressed
|
|
31
|
-
*/
|
|
32
|
-
onPrivacyPress?: () => void;
|
|
33
|
-
/**
|
|
34
|
-
* Callback when Terms of Service is pressed
|
|
35
|
-
*/
|
|
36
|
-
onTermsPress?: () => void;
|
|
37
|
-
/**
|
|
38
|
-
* Additional styles
|
|
39
|
-
*/
|
|
40
|
-
style?: object;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export const LegalLinks: React.FC<LegalLinksProps> = React.memo(
|
|
44
|
-
({
|
|
45
|
-
privacyPolicyUrl,
|
|
46
|
-
termsOfServiceUrl,
|
|
47
|
-
privacyText,
|
|
48
|
-
termsText,
|
|
49
|
-
onPrivacyPress,
|
|
50
|
-
onTermsPress,
|
|
51
|
-
style,
|
|
52
|
-
}) => {
|
|
53
|
-
// Memoize press handlers to prevent child re-renders
|
|
54
|
-
const handlePrivacyPress = React.useCallback(async () => {
|
|
55
|
-
if (onPrivacyPress) {
|
|
56
|
-
onPrivacyPress();
|
|
57
|
-
} else if (privacyPolicyUrl) {
|
|
58
|
-
try {
|
|
59
|
-
await UrlHandlerService.openUrl(privacyPolicyUrl);
|
|
60
|
-
} catch {
|
|
61
|
-
// Silent error handling
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}, [onPrivacyPress, privacyPolicyUrl]);
|
|
65
|
-
|
|
66
|
-
const handleTermsPress = React.useCallback(async () => {
|
|
67
|
-
if (onTermsPress) {
|
|
68
|
-
onTermsPress();
|
|
69
|
-
} else if (termsOfServiceUrl) {
|
|
70
|
-
try {
|
|
71
|
-
await UrlHandlerService.openUrl(termsOfServiceUrl);
|
|
72
|
-
} catch {
|
|
73
|
-
// Silent error handling
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}, [onTermsPress, termsOfServiceUrl]);
|
|
77
|
-
|
|
78
|
-
// Direct boolean calculations - no need for useMemo overhead
|
|
79
|
-
const showPrivacy = !!(onPrivacyPress || privacyPolicyUrl);
|
|
80
|
-
const showTerms = !!(onTermsPress || termsOfServiceUrl);
|
|
81
|
-
const showSeparator = showPrivacy && showTerms;
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
<View style={[styles.container, style]}>
|
|
85
|
-
{showPrivacy && (
|
|
86
|
-
<TouchableOpacity onPress={handlePrivacyPress} hitSlop={styles.hitSlop}>
|
|
87
|
-
<AtomicText
|
|
88
|
-
type="labelSmall"
|
|
89
|
-
color="primary"
|
|
90
|
-
style={styles.link}
|
|
91
|
-
>
|
|
92
|
-
{privacyText}
|
|
93
|
-
</AtomicText>
|
|
94
|
-
</TouchableOpacity>
|
|
95
|
-
)}
|
|
96
|
-
{showSeparator && (
|
|
97
|
-
<AtomicText
|
|
98
|
-
type="labelSmall"
|
|
99
|
-
color="textTertiary"
|
|
100
|
-
>
|
|
101
|
-
{" • "}
|
|
102
|
-
</AtomicText>
|
|
103
|
-
)}
|
|
104
|
-
{showTerms && (
|
|
105
|
-
<TouchableOpacity onPress={handleTermsPress} hitSlop={styles.hitSlop}>
|
|
106
|
-
<AtomicText
|
|
107
|
-
type="labelSmall"
|
|
108
|
-
color="primary"
|
|
109
|
-
style={styles.link}
|
|
110
|
-
>
|
|
111
|
-
{termsText}
|
|
112
|
-
</AtomicText>
|
|
113
|
-
</TouchableOpacity>
|
|
114
|
-
)}
|
|
115
|
-
</View>
|
|
116
|
-
);
|
|
117
|
-
},
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
LegalLinks.displayName = "LegalLinks";
|
|
121
|
-
|
|
122
|
-
const styles = StyleSheet.create({
|
|
123
|
-
container: {
|
|
124
|
-
flexDirection: "row",
|
|
125
|
-
alignItems: "center",
|
|
126
|
-
justifyContent: "center",
|
|
127
|
-
},
|
|
128
|
-
link: {
|
|
129
|
-
textDecorationLine: "underline",
|
|
130
|
-
},
|
|
131
|
-
hitSlop: {
|
|
132
|
-
top: 10,
|
|
133
|
-
bottom: 10,
|
|
134
|
-
left: 10,
|
|
135
|
-
right: 10,
|
|
136
|
-
},
|
|
137
|
-
});
|