@umituz/react-native-settings 4.23.80 → 4.23.82

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "4.23.80",
3
+ "version": "4.23.82",
4
4
  "description": "Complete settings hub for React Native apps - consolidated package with settings, localization, about, legal, appearance, feedback, FAQs, rating, and gamification",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -41,14 +41,12 @@
41
41
  "firebase": "^12.7.0"
42
42
  },
43
43
  "peerDependencies": {
44
- "expo": ">=54.0.0",
45
44
  "@expo/vector-icons": ">=14.0.0",
46
- "@react-native-async-storage/async-storage": ">=2.0.0",
47
- "@react-native-community/datetimepicker": ">=8.0.0",
48
45
  "@react-navigation/native": ">=6.0.0",
49
46
  "@react-navigation/stack": ">=6.0.0",
50
47
  "@tanstack/react-query": ">=5.0.0",
51
48
  "@umituz/react-native-design-system": "*",
49
+ "expo": ">=54.0.0",
52
50
  "expo-device": ">=6.0.0",
53
51
  "expo-haptics": ">=15.0.0",
54
52
  "expo-localization": ">=16.0.0",
@@ -65,7 +63,7 @@
65
63
  "@expo/vector-icons": "^15.0.0",
66
64
  "@gorhom/bottom-sheet": "^5.2.8",
67
65
  "@react-native-async-storage/async-storage": "^2.2.0",
68
- "@react-native-community/datetimepicker": "^8.2.0",
66
+ "@react-native-community/datetimepicker": "^8.6.0",
69
67
  "@react-native-community/slider": "^5.1.1",
70
68
  "@react-navigation/bottom-tabs": "^7.9.0",
71
69
  "@react-navigation/native": "^7.1.26",
@@ -77,7 +75,7 @@
77
75
  "@typescript-eslint/eslint-plugin": "^7.18.0",
78
76
  "@typescript-eslint/parser": "^7.18.0",
79
77
  "@umituz/react-native-auth": "^3.6.49",
80
- "@umituz/react-native-design-system": "^4.23.58",
78
+ "@umituz/react-native-design-system": "*",
81
79
  "@umituz/react-native-firebase": "^1.13.102",
82
80
  "@umituz/react-native-sentry": "*",
83
81
  "eslint": "^8.57.0",
@@ -115,7 +113,6 @@
115
113
  "react-native-reanimated": "^4.2.1",
116
114
  "react-native-safe-area-context": "^5.6.2",
117
115
  "react-native-svg": "^15.15.1",
118
- "rn-emoji-keyboard": "^1.7.0",
119
116
  "typescript": "^5.3.0",
120
117
  "zustand": "^5.0.9"
121
118
  },
@@ -4,8 +4,10 @@
4
4
  * Fully configurable and generic
5
5
  * Optimized for performance and memory safety
6
6
  */
7
- import React from 'react';
8
- import { AboutScreenContainer } from './AboutScreenContainer';
7
+ import { ScreenLayout, NavigationHeader, useAppDesignTokens, useAppNavigation, AtomicText, AtomicSpinner } from '@umituz/react-native-design-system';
8
+ import { useLocalization } from '../../../localization';
9
+ import { useAboutInfo } from '../hooks/useAboutInfo';
10
+ import { AboutScreenContent } from './AboutScreenContent';
9
11
 
10
12
  export interface AboutScreenProps {
11
13
  /** Configuration for the about screen */
@@ -29,5 +31,49 @@ export interface AboutScreenProps {
29
31
  }
30
32
 
31
33
  export const AboutScreen: React.FC<AboutScreenProps> = (props) => {
32
- return <AboutScreenContainer {...props} />;
34
+ const { config, testID = 'about-screen' } = props;
35
+ const tokens = useAppDesignTokens();
36
+ const navigation = useAppNavigation();
37
+ const { t } = useLocalization();
38
+
39
+ const { appInfo, loading, error } = useAboutInfo({
40
+ autoInit: true,
41
+ initialConfig: config,
42
+ });
43
+
44
+ const header = (
45
+ <NavigationHeader
46
+ title={t("settings.about.title")}
47
+ onBackPress={() => navigation.goBack()}
48
+ />
49
+ );
50
+
51
+ if (loading) {
52
+ return (
53
+ <ScreenLayout header={header} testID={testID}>
54
+ <AtomicSpinner fullContainer size="lg" />
55
+ </ScreenLayout>
56
+ );
57
+ }
58
+
59
+ if (error || !appInfo) {
60
+ const errorText = error ? `${config.texts?.errorPrefix || 'Error:'} ${error}` : (config.texts?.noInfo || 'No info available');
61
+ return (
62
+ <ScreenLayout header={header} testID={testID}>
63
+ <AtomicText type="bodyMedium" color="error" style={{ textAlign: 'center', marginTop: 20 }}>
64
+ {errorText}
65
+ </AtomicText>
66
+ </ScreenLayout>
67
+ );
68
+ }
69
+
70
+ return (
71
+ <ScreenLayout header={header} testID={testID}>
72
+ <AboutScreenContent
73
+ {...props}
74
+ appInfo={appInfo}
75
+ _tokens={tokens}
76
+ />
77
+ </ScreenLayout>
78
+ );
33
79
  };
@@ -7,7 +7,6 @@ import React, { useMemo, useCallback } from 'react';
7
7
  import {
8
8
  View,
9
9
  StyleSheet,
10
- ScrollView,
11
10
  } from 'react-native';
12
11
  import { AboutHeader } from '../components/AboutHeader';
13
12
  import { AboutContent } from '../components/AboutContent';
@@ -98,15 +97,14 @@ export const AboutScreenContent: React.FC<AboutScreenContentProps> = ({
98
97
  }, [containerStyle, colors.backgroundPrimary, styles]);
99
98
 
100
99
  return (
101
- <ScrollView
100
+ <View
102
101
  style={containerStyles}
103
102
  testID={testID}
104
- contentContainerStyle={{ paddingBottom: 32 }}
105
103
  >
106
104
  {renderHeader()}
107
105
  {renderContent()}
108
106
  {renderFooter()}
109
- </ScrollView>
107
+ </View>
110
108
  );
111
109
  };
112
110
 
@@ -1,5 +1,5 @@
1
- // Providers
2
- export { LocalizationProvider } from './presentation/providers/LocalizationProvider';
1
+ // Managers
2
+ export { LocalizationManager } from './presentation/providers/LocalizationManager';
3
3
 
4
4
  // Hooks
5
5
  export { useLocalization } from './infrastructure/hooks/useLocalization';
@@ -13,7 +13,7 @@ interface LocalizationProviderProps {
13
13
  defaultLanguage?: string;
14
14
  }
15
15
 
16
- export const LocalizationProvider: React.FC<LocalizationProviderProps> = ({
16
+ export const LocalizationManager: React.FC<LocalizationProviderProps> = ({
17
17
  children,
18
18
  translations,
19
19
  defaultLanguage = "en-US",
@@ -43,7 +43,7 @@ export const LanguageSelectionScreen: React.FC<LanguageSelectionScreenProps> = (
43
43
  }
44
44
  await handleLanguageSelect(code, () => {
45
45
  if (__DEV__) {
46
- console.log('[LanguageSelectionScreen] Navigating back using context navigation');
46
+ console.log('[LanguageSelectionScreen] Navigating back using standard navigation');
47
47
  }
48
48
  navigation.goBack();
49
49
  });
@@ -8,10 +8,9 @@ import {
8
8
  ScreenLayout,
9
9
  useAppNavigation,
10
10
  } from "@umituz/react-native-design-system";
11
+ import { useSettingsScreen } from "./hooks/useSettingsScreen";
11
12
  import { SettingsHeader } from "./components/SettingsHeader";
12
13
  import { SettingsContent } from "./components/SettingsContent";
13
- import { normalizeSettingsConfig } from "./utils/normalizeConfig";
14
- import { useFeatureDetection } from "./hooks/useFeatureDetection";
15
14
  import type { SettingsConfig, CustomSettingsSection } from "./types";
16
15
  import type { DevSettingsProps } from "../../domains/dev";
17
16
 
@@ -55,63 +54,44 @@ export interface SettingsScreenProps {
55
54
  showHeader?: boolean;
56
55
  }
57
56
 
58
- export const SettingsScreen: React.FC<SettingsScreenProps> = ({
59
- children,
60
- config = {},
61
- showUserProfile,
62
- userProfile,
63
- showFooter = true,
64
- footerText,
65
- appVersion,
66
- customSections = [],
67
- showCloseButton = false,
68
- showHeader = true,
69
- onClose,
70
- featureOptions,
71
- devSettings,
72
- gamificationConfig,
73
- }) => {
74
- const navigation = useAppNavigation();
57
+ export const SettingsScreen: React.FC<SettingsScreenProps> = (props) => {
58
+ const {
59
+ children,
60
+ config = {},
61
+ showUserProfile,
62
+ userProfile,
63
+ showFooter = true,
64
+ footerText,
65
+ appVersion: providedVersion,
66
+ customSections = [],
67
+ showCloseButton = false,
68
+ showHeader = true,
69
+ onClose,
70
+ featureOptions,
71
+ devSettings,
72
+ gamificationConfig,
73
+ } = props;
75
74
 
76
- // Memoize config normalization to prevent unnecessary recalculations
77
- const normalizedConfig = React.useMemo(
78
- () => normalizeSettingsConfig(config),
79
- [config]
80
- );
81
-
82
- // Feature detection hook (must be called at top level)
83
- const detectedFeatures = useFeatureDetection(normalizedConfig, navigation, featureOptions);
84
-
85
- // Memoize features to prevent unnecessary recalculations
86
- const features = React.useMemo(
87
- () => detectedFeatures,
88
- [detectedFeatures]
89
- );
90
-
91
- // Determine if user profile should be shown (explicit prop takes priority, then config)
92
- const shouldShowUserProfile = showUserProfile ?? features.userProfile;
75
+ const {
76
+ normalizedConfig,
77
+ features,
78
+ shouldShowUserProfile,
79
+ appVersion,
80
+ handleClose,
81
+ } = useSettingsScreen({
82
+ config,
83
+ showUserProfile,
84
+ featureOptions,
85
+ appVersion: providedVersion,
86
+ onClose,
87
+ });
93
88
 
94
- // Workaround: Use conditional rendering with type assertion
95
- if (showHeader) {
96
- return <ScreenLayout header={<SettingsHeader showCloseButton={showCloseButton} onClose={onClose} />}>
97
- {children ?? (
98
- <SettingsContent
99
- normalizedConfig={normalizedConfig}
100
- features={features}
101
- showUserProfile={shouldShowUserProfile}
102
- userProfile={userProfile}
103
- showFooter={showFooter}
104
- footerText={footerText}
105
- appVersion={appVersion}
106
- customSections={customSections}
107
- devSettings={devSettings}
108
- gamificationConfig={gamificationConfig}
109
- />
110
- )}
111
- </ScreenLayout>;
112
- }
89
+ const header = showHeader ? (
90
+ <SettingsHeader showCloseButton={showCloseButton} onClose={handleClose} />
91
+ ) : undefined;
113
92
 
114
- return <ScreenLayout>
93
+ return (
94
+ <ScreenLayout header={header}>
115
95
  {children ?? (
116
96
  <SettingsContent
117
97
  normalizedConfig={normalizedConfig}
@@ -126,5 +106,6 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
126
106
  gamificationConfig={gamificationConfig}
127
107
  />
128
108
  )}
129
- </ScreenLayout>;
109
+ </ScreenLayout>
110
+ );
130
111
  };
@@ -3,9 +3,7 @@
3
3
  * Handles close button functionality
4
4
  */
5
5
 
6
- import React from "react";
7
- import { View, Pressable, StyleSheet } from "react-native";
8
- import { useAppDesignTokens, AtomicIcon, AtomicText, useAppNavigation } from "@umituz/react-native-design-system";
6
+ import { useAppDesignTokens, AtomicIcon, useAppNavigation, NavigationHeader } from "@umituz/react-native-design-system";
9
7
  import { useLocalization } from "../../../domains/localization";
10
8
 
11
9
  interface SettingsHeaderProps {
@@ -29,41 +27,32 @@ export const SettingsHeader: React.FC<SettingsHeaderProps> = ({
29
27
  }
30
28
  };
31
29
 
30
+ const rightElement = showCloseButton ? (
31
+ <Pressable
32
+ onPress={handleClose}
33
+ style={({ pressed }) => [
34
+ {
35
+ width: 44,
36
+ height: 44,
37
+ justifyContent: "center",
38
+ alignItems: "center",
39
+ backgroundColor: pressed ? tokens.colors.surfaceVariant : tokens.colors.surface,
40
+ borderRadius: tokens.borders.radius.full,
41
+ },
42
+ ]}
43
+ hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
44
+ >
45
+ <AtomicIcon name="close-outline" size="lg" color="textPrimary" />
46
+ </Pressable>
47
+ ) : undefined;
48
+
32
49
  return (
33
- <View style={[styles.container, { padding: tokens.spacing.lg }]}>
34
- <AtomicText type="headlineLarge">
35
- {t('settings.title')}
36
- </AtomicText>
37
-
38
- {showCloseButton && (
39
- <Pressable
40
- onPress={handleClose}
41
- style={({ pressed }) => [
42
- styles.closeButton,
43
- {
44
- backgroundColor: pressed ? tokens.colors.surfaceVariant : tokens.colors.surface,
45
- borderRadius: tokens.borders.radius.full,
46
- },
47
- ]}
48
- hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
49
- >
50
- <AtomicIcon name="close-outline" size="lg" color="textPrimary" />
51
- </Pressable>
52
- )}
53
- </View>
50
+ <NavigationHeader
51
+ title={t('settings.title')}
52
+ rightElement={rightElement}
53
+ // If NOT showing close button, we might want a back button?
54
+ // But usually Settings is a root screen in a modal or stack.
55
+ onBackPress={!showCloseButton ? handleClose : undefined}
56
+ />
54
57
  );
55
- };
56
-
57
- const styles = StyleSheet.create({
58
- container: {
59
- flexDirection: 'row',
60
- justifyContent: 'space-between',
61
- alignItems: 'center',
62
- },
63
- closeButton: {
64
- width: 44,
65
- height: 44,
66
- justifyContent: "center",
67
- alignItems: "center",
68
- },
69
- });
58
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * useSettingsScreen Hook
3
+ * Refactored: Extracted business logic from SettingsScreen component
4
+ */
5
+
6
+ import { useMemo, useCallback } from "react";
7
+ import { useAppNavigation } from "@umituz/react-native-design-system";
8
+ import { normalizeSettingsConfig } from "../utils/normalizeConfig";
9
+ import { useFeatureDetection } from "./useFeatureDetection";
10
+ import { getAppVersion } from "../../../utils/appUtils";
11
+ import type { SettingsConfig } from "../types";
12
+
13
+ export interface UseSettingsScreenParams {
14
+ config?: SettingsConfig;
15
+ showUserProfile?: boolean;
16
+ featureOptions?: {
17
+ notificationServiceAvailable?: boolean;
18
+ };
19
+ appVersion?: string;
20
+ onClose?: () => void;
21
+ }
22
+
23
+ export function useSettingsScreen({
24
+ config = {},
25
+ showUserProfile,
26
+ featureOptions,
27
+ appVersion: providedVersion,
28
+ onClose,
29
+ }: UseSettingsScreenParams) {
30
+ const navigation = useAppNavigation();
31
+
32
+ // Normalize config
33
+ const normalizedConfig = useMemo(
34
+ () => normalizeSettingsConfig(config),
35
+ [config]
36
+ );
37
+
38
+ // Feature detection
39
+ const features = useFeatureDetection(
40
+ normalizedConfig,
41
+ navigation,
42
+ featureOptions
43
+ );
44
+
45
+ const shouldShowUserProfile = showUserProfile ?? features.userProfile;
46
+
47
+ const appVersion = providedVersion || getAppVersion();
48
+
49
+ const handleClose = useCallback(() => {
50
+ if (onClose) {
51
+ onClose();
52
+ } else {
53
+ navigation.goBack();
54
+ }
55
+ }, [onClose, navigation]);
56
+
57
+ return {
58
+ normalizedConfig,
59
+ features,
60
+ shouldShowUserProfile,
61
+ appVersion,
62
+ handleClose,
63
+ };
64
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * App and Platform Utilities
3
+ */
4
+ import { Platform } from "react-native";
5
+ import Constants from "expo-constants";
6
+
7
+ /**
8
+ * Gets the current app version from Expo constants
9
+ */
10
+ export function getAppVersion(): string {
11
+ const version = Constants.expoConfig?.version ?? Constants.manifest2?.extra?.expoClient?.version;
12
+ if (!version) {
13
+ // Return a default if not found
14
+ return "1.0.0";
15
+ }
16
+ return version;
17
+ }
18
+
19
+ /**
20
+ * Validates if the current platform is supported
21
+ */
22
+ export function validatePlatform(): "ios" | "android" {
23
+ const platform = Platform.OS;
24
+ if (platform !== "ios" && platform !== "android") {
25
+ // Default to android for consistency if something goes wrong in detection
26
+ return "android";
27
+ }
28
+ return platform;
29
+ }
30
+
31
+ /**
32
+ * Checks if the app is currently in development mode
33
+ */
34
+ export function isDev(): boolean {
35
+ return __DEV__;
36
+ }
@@ -0,0 +1 @@
1
+ export * from "./appUtils";
@@ -1,109 +0,0 @@
1
- /**
2
- * AboutScreen Container Component
3
- * Handles business logic and state management for About screen
4
- */
5
- import React from 'react';
6
- import { View, StyleSheet } from 'react-native';
7
- import { AboutScreenContent } from './AboutScreenContent';
8
- import { useAboutInfo } from '../hooks/useAboutInfo';
9
- import { useAppDesignTokens, AtomicText, type DesignTokens } from '@umituz/react-native-design-system';
10
- import type { AboutScreenProps } from './AboutScreen';
11
-
12
- export const AboutScreenContainer: React.FC<AboutScreenProps> = ({
13
- config,
14
- containerStyle,
15
- headerStyle,
16
- titleStyle,
17
- versionStyle,
18
- showHeader = true,
19
- headerComponent,
20
- footerComponent,
21
- testID,
22
- }) => {
23
- const tokens = useAppDesignTokens();
24
- const styles = getStyles(tokens);
25
-
26
- const { appInfo, loading, error } = useAboutInfo({
27
- autoInit: true,
28
- initialConfig: config,
29
- });
30
-
31
- const containerStyles = React.useMemo(() => {
32
- return [
33
- styles.container,
34
- { backgroundColor: tokens.colors.backgroundPrimary },
35
- containerStyle
36
- ];
37
- }, [containerStyle, tokens.colors.backgroundPrimary, styles]);
38
-
39
- const texts = config.texts || {};
40
-
41
- if (loading) {
42
- return (
43
- <View style={containerStyles} testID={testID}>
44
- <AtomicText
45
- type="bodyMedium"
46
- color="textSecondary"
47
- style={styles.statusText}
48
- >
49
- {texts.loading}
50
- </AtomicText>
51
- </View>
52
- );
53
- }
54
-
55
- if (error) {
56
- return (
57
- <View style={containerStyles} testID={testID}>
58
- <AtomicText
59
- type="bodyMedium"
60
- color="error"
61
- style={styles.statusText}
62
- >
63
- {texts.errorPrefix} {error}
64
- </AtomicText>
65
- </View>
66
- );
67
- }
68
-
69
- if (!appInfo) {
70
- return (
71
- <View style={containerStyles} testID={testID}>
72
- <AtomicText
73
- type="bodyMedium"
74
- color="textSecondary"
75
- style={styles.statusText}
76
- >
77
- {texts.noInfo}
78
- </AtomicText>
79
- </View>
80
- );
81
- }
82
-
83
- return (
84
- <AboutScreenContent
85
- appInfo={appInfo}
86
- config={config}
87
- containerStyle={containerStyle}
88
- headerStyle={headerStyle}
89
- titleStyle={titleStyle}
90
- versionStyle={versionStyle}
91
- showHeader={showHeader}
92
- headerComponent={headerComponent}
93
- footerComponent={footerComponent}
94
- testID={testID}
95
- _tokens={tokens}
96
- />
97
- );
98
- };
99
-
100
- const getStyles = (tokens: DesignTokens) => StyleSheet.create({
101
- container: {
102
- flex: 1,
103
- },
104
- statusText: {
105
- textAlign: 'center',
106
- fontSize: tokens.typography.bodyMedium.responsiveFontSize,
107
- marginTop: 20,
108
- },
109
- });