@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.
Files changed (70) hide show
  1. package/package.json +2 -4
  2. package/src/domains/about/presentation/screens/AboutScreenContent.tsx +87 -63
  3. package/src/domains/appearance/data/colorPalettes.ts +0 -23
  4. package/src/domains/appearance/presentation/components/CustomColorsSection.tsx +2 -4
  5. package/src/domains/appearance/presentation/components/ThemeOption.tsx +2 -2
  6. package/src/domains/dev/presentation/components/DevSettingsSection.tsx +5 -2
  7. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +19 -25
  8. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +160 -81
  9. package/src/domains/gamification/components/GamificationScreen/GamificationScreenWithConfig.tsx +11 -11
  10. package/src/domains/localization/infrastructure/components/LanguageSwitcher.tsx +0 -2
  11. package/src/domains/localization/infrastructure/storage/localizationStoreUtils.ts +1 -1
  12. package/src/domains/localization/presentation/screens/LanguageSelectionScreen.tsx +85 -48
  13. package/src/domains/localization/presentation/screens/__tests__/LanguageSelectionScreen.test.tsx +0 -15
  14. package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +1 -3
  15. package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +0 -4
  16. package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +69 -31
  17. package/src/domains/rating/presentation/components/StarRating.tsx +7 -13
  18. package/src/infrastructure/utils/configFactory.ts +0 -26
  19. package/src/infrastructure/utils/constants/textLimits.ts +0 -2
  20. package/src/infrastructure/utils/sanitizers.ts +1 -25
  21. package/src/infrastructure/utils/validation/core.ts +0 -33
  22. package/src/infrastructure/utils/validation/formValidators.ts +7 -1
  23. package/src/infrastructure/utils/validation/index.ts +2 -33
  24. package/src/infrastructure/utils/validators.ts +0 -6
  25. package/src/presentation/navigation/utils/index.ts +1 -7
  26. package/src/presentation/navigation/utils/navigationHelpers.ts +2 -87
  27. package/src/presentation/screens/components/SettingsContent.tsx +4 -19
  28. package/src/presentation/screens/components/sections/CustomSettingsList.tsx +3 -8
  29. package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +0 -4
  30. package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +0 -4
  31. package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +0 -4
  32. package/src/presentation/utils/screenFactory.ts +0 -25
  33. package/src/utils/appUtils.ts +0 -18
  34. package/src/utils/devUtils.ts +0 -10
  35. package/src/utils/errorUtils.ts +0 -22
  36. package/src/domains/about/utils/index.ts +0 -156
  37. package/src/domains/faqs/domain/services/index.ts +0 -1
  38. package/src/domains/faqs/presentation/screens/index.ts +0 -2
  39. package/src/domains/gamification/components/GamificationScreen/Header.tsx +0 -30
  40. package/src/domains/legal/presentation/components/LegalLinks.tsx +0 -137
  41. package/src/domains/legal/presentation/components/index.ts +0 -5
  42. package/src/domains/localization/infrastructure/config/languagesData.ts +0 -26
  43. package/src/domains/localization/infrastructure/hooks/TranslationHook.ts +0 -37
  44. package/src/domains/localization/infrastructure/storage/AsyncStorageWrapper.ts +0 -34
  45. package/src/infrastructure/storage/storeConfig.ts +0 -114
  46. package/src/infrastructure/types/commonComponentTypes.ts +0 -142
  47. package/src/infrastructure/utils/async/core.ts +0 -110
  48. package/src/infrastructure/utils/async/debounceAndBatch.ts +0 -69
  49. package/src/infrastructure/utils/async/index.ts +0 -8
  50. package/src/infrastructure/utils/async/retryAndTimeout.ts +0 -65
  51. package/src/infrastructure/utils/dateUtils.ts +0 -61
  52. package/src/infrastructure/utils/errorHandlers.ts +0 -250
  53. package/src/infrastructure/utils/index.ts +0 -12
  54. package/src/infrastructure/utils/memoComparisonUtils.ts +0 -66
  55. package/src/infrastructure/utils/memoUtils.ts +0 -167
  56. package/src/infrastructure/utils/styleTokens.ts +0 -145
  57. package/src/infrastructure/utils/styles/componentStyles.ts +0 -90
  58. package/src/infrastructure/utils/styles/index.ts +0 -9
  59. package/src/infrastructure/utils/styles/layoutStyles.ts +0 -56
  60. package/src/infrastructure/utils/styles/spacingStyles.ts +0 -33
  61. package/src/infrastructure/utils/styles/styleHelpers.ts +0 -22
  62. package/src/infrastructure/utils/translationHelpers.ts +0 -81
  63. package/src/infrastructure/utils/validation/numericValidators.ts +0 -66
  64. package/src/infrastructure/utils/validation/passwordValidator.ts +0 -53
  65. package/src/infrastructure/utils/validation/textValidators.ts +0 -118
  66. package/src/presentation/components/ErrorBoundary/SettingsErrorBoundary.tsx +0 -105
  67. package/src/presentation/components/ErrorBoundary/index.ts +0 -12
  68. package/src/presentation/components/ErrorBoundary/withErrorBoundary.tsx +0 -45
  69. package/src/utils/hooks/index.ts +0 -6
  70. package/src/utils/index.ts +0 -3
@@ -51,9 +51,3 @@ export function validateSwitchProps(
51
51
  return null;
52
52
  }
53
53
 
54
- /**
55
- * Return validation result (no logging)
56
- */
57
- export function getValidationResult(warning: ValidationWarning | null): ValidationWarning | null {
58
- return warning;
59
- }
@@ -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
- export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({ customSections = [] }) => {
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
  */
@@ -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
 
@@ -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
- };
@@ -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,2 +0,0 @@
1
- export { FAQScreen } from "./FAQScreen";
2
- export type { FAQScreenProps } from "./FAQScreen";
@@ -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
- });