@umituz/react-native-settings 4.20.25 → 4.20.27
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 -3
- package/src/index.ts +7 -2
- package/src/presentation/navigation/SettingsStackNavigator.tsx +150 -136
- package/src/presentation/navigation/hooks/index.ts +5 -0
- package/src/presentation/navigation/hooks/useNavigationHandlers.ts +58 -0
- package/src/presentation/navigation/types.ts +88 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-settings",
|
|
3
|
-
"version": "4.20.
|
|
3
|
+
"version": "4.20.27",
|
|
4
4
|
"description": "Complete settings hub for React Native apps - consolidated package with settings, about, legal, appearance, feedback, FAQs, and rating",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -36,8 +36,6 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
39
|
-
"@react-native-firebase/app": "^23.7.0",
|
|
40
|
-
"@react-native-firebase/firestore": "^23.7.0",
|
|
41
39
|
"@umituz/react-native-auth": "latest",
|
|
42
40
|
"@umituz/react-native-design-system": "latest",
|
|
43
41
|
"@umituz/react-native-localization": "latest",
|
package/src/index.ts
CHANGED
|
@@ -53,8 +53,13 @@ export { AppearanceScreen } from './presentation/screens/AppearanceScreen';
|
|
|
53
53
|
export { SettingsStackNavigator } from './presentation/navigation/SettingsStackNavigator';
|
|
54
54
|
export type {
|
|
55
55
|
SettingsStackNavigatorProps,
|
|
56
|
-
SettingsStackParamList
|
|
57
|
-
|
|
56
|
+
SettingsStackParamList,
|
|
57
|
+
AppInfo,
|
|
58
|
+
LegalUrls,
|
|
59
|
+
UserProfileConfig,
|
|
60
|
+
AdditionalScreen,
|
|
61
|
+
FAQData,
|
|
62
|
+
} from './presentation/navigation/types';
|
|
58
63
|
|
|
59
64
|
// =============================================================================
|
|
60
65
|
// PRESENTATION LAYER - Types
|
|
@@ -1,152 +1,166 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Settings Stack Navigator
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Complete settings navigation with all screens.
|
|
5
|
+
* Receives appInfo and legalUrls from app.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useMemo } from "react";
|
|
9
9
|
import { createStackNavigator } from "@react-navigation/stack";
|
|
10
10
|
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
11
|
+
import { useLocalization } from "@umituz/react-native-localization";
|
|
11
12
|
import { SettingsScreen } from "../screens/SettingsScreen";
|
|
12
13
|
import { AppearanceScreen } from "../screens/AppearanceScreen";
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export type SettingsStackParamList = {
|
|
19
|
-
Settings: { config?: SettingsConfig };
|
|
20
|
-
Appearance: undefined;
|
|
21
|
-
// About and Legal screens will be handled by external packages
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export interface SettingsStackNavigatorProps {
|
|
25
|
-
/**
|
|
26
|
-
* Settings configuration
|
|
27
|
-
*/
|
|
28
|
-
config?: SettingsConfig;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* App version number from app config (e.g., "1.0.0")
|
|
32
|
-
*/
|
|
33
|
-
appVersion?: string;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Show user profile header
|
|
37
|
-
*/
|
|
38
|
-
showUserProfile?: boolean;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* User profile props for anonymous/authenticated users
|
|
42
|
-
*/
|
|
43
|
-
userProfile?: {
|
|
44
|
-
displayName?: string;
|
|
45
|
-
userId?: string;
|
|
46
|
-
isAnonymous?: boolean;
|
|
47
|
-
avatarUrl?: string;
|
|
48
|
-
accountSettingsRoute?: string;
|
|
49
|
-
onPress?: () => void;
|
|
50
|
-
anonymousDisplayName?: string;
|
|
51
|
-
avatarServiceUrl?: string;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Additional screens to register
|
|
56
|
-
* Apps can add their own screens here
|
|
57
|
-
*/
|
|
58
|
-
additionalScreens?: Array<{
|
|
59
|
-
name: string;
|
|
60
|
-
component: React.ComponentType<any>;
|
|
61
|
-
options?: any;
|
|
62
|
-
}>;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Dev settings (only shown in __DEV__ mode)
|
|
66
|
-
*/
|
|
67
|
-
devSettings?: DevSettingsProps;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Custom sections to render in settings list
|
|
71
|
-
* Use this to add app-specific items like subscription
|
|
72
|
-
*/
|
|
73
|
-
customSections?: CustomSettingsSection[];
|
|
74
|
-
}
|
|
14
|
+
import { LegalScreen } from "../../domains/legal";
|
|
15
|
+
import { AboutScreen } from "../../domains/about";
|
|
16
|
+
import { FAQScreen } from "../../domains/faqs";
|
|
17
|
+
import { useNavigationHandlers } from "./hooks";
|
|
18
|
+
import type { SettingsStackParamList, SettingsStackNavigatorProps } from "./types";
|
|
75
19
|
|
|
76
20
|
const Stack = createStackNavigator<SettingsStackParamList>();
|
|
77
21
|
|
|
78
22
|
export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
23
|
+
appInfo,
|
|
24
|
+
legalUrls,
|
|
25
|
+
faqData,
|
|
26
|
+
config = {},
|
|
27
|
+
showUserProfile = false,
|
|
28
|
+
userProfile,
|
|
29
|
+
additionalScreens = [],
|
|
30
|
+
devSettings,
|
|
31
|
+
customSections = [],
|
|
86
32
|
}) => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return (
|
|
120
|
-
<Stack.Navigator screenOptions={screenOptions}>
|
|
121
|
-
<Stack.Screen
|
|
122
|
-
name="Settings"
|
|
123
|
-
component={SettingsScreenWrapper}
|
|
124
|
-
options={{
|
|
125
|
-
headerShown: false,
|
|
126
|
-
title: "Settings",
|
|
127
|
-
}}
|
|
128
|
-
/>
|
|
129
|
-
|
|
130
|
-
<Stack.Screen
|
|
131
|
-
name="Appearance"
|
|
132
|
-
component={AppearanceScreen}
|
|
133
|
-
options={{
|
|
134
|
-
headerShown: true,
|
|
135
|
-
headerTitle: "Appearance",
|
|
136
|
-
headerTitleAlign: "center",
|
|
137
|
-
headerBackTitle: "Settings",
|
|
138
|
-
}}
|
|
139
|
-
/>
|
|
140
|
-
|
|
141
|
-
{/* Render additional screens */}
|
|
142
|
-
{additionalScreens.map((screen) => (
|
|
143
|
-
<Stack.Screen
|
|
144
|
-
key={screen.name}
|
|
145
|
-
name={screen.name as any}
|
|
146
|
-
component={screen.component}
|
|
147
|
-
options={screen.options}
|
|
148
|
-
/>
|
|
149
|
-
))}
|
|
150
|
-
</Stack.Navigator>
|
|
33
|
+
const tokens = useAppDesignTokens();
|
|
34
|
+
const { t } = useLocalization();
|
|
35
|
+
const { handlePrivacyPress, handleTermsPress, handleEulaPress, aboutConfig } =
|
|
36
|
+
useNavigationHandlers(appInfo, legalUrls);
|
|
37
|
+
|
|
38
|
+
const screenOptions = useMemo(
|
|
39
|
+
() => ({
|
|
40
|
+
headerStyle: {
|
|
41
|
+
backgroundColor: tokens.colors.surface,
|
|
42
|
+
borderBottomColor: tokens.colors.borderLight,
|
|
43
|
+
borderBottomWidth: 1,
|
|
44
|
+
},
|
|
45
|
+
headerTitleStyle: {
|
|
46
|
+
fontSize: 18,
|
|
47
|
+
fontWeight: "600" as const,
|
|
48
|
+
color: tokens.colors.textPrimary,
|
|
49
|
+
},
|
|
50
|
+
headerTintColor: tokens.colors.textPrimary,
|
|
51
|
+
}),
|
|
52
|
+
[tokens]
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const SettingsScreenWrapper = useMemo(() => {
|
|
56
|
+
const Wrapper = () => (
|
|
57
|
+
<SettingsScreen
|
|
58
|
+
config={config}
|
|
59
|
+
appVersion={appInfo.version}
|
|
60
|
+
showUserProfile={showUserProfile}
|
|
61
|
+
userProfile={userProfile}
|
|
62
|
+
devSettings={devSettings}
|
|
63
|
+
customSections={customSections}
|
|
64
|
+
/>
|
|
151
65
|
);
|
|
66
|
+
Wrapper.displayName = "SettingsScreenWrapper";
|
|
67
|
+
return Wrapper;
|
|
68
|
+
}, [config, appInfo.version, showUserProfile, userProfile, devSettings, customSections]);
|
|
69
|
+
|
|
70
|
+
const LegalScreenWrapper = useMemo(() => {
|
|
71
|
+
const Wrapper = () => (
|
|
72
|
+
<LegalScreen
|
|
73
|
+
title={t("settings.legal.title")}
|
|
74
|
+
description={t("settings.legal.description")}
|
|
75
|
+
documentsHeader={t("settings.legal.documentsHeader")}
|
|
76
|
+
privacyTitle={t("settings.legal.privacyTitle")}
|
|
77
|
+
privacyDescription={t("settings.legal.privacyDescription")}
|
|
78
|
+
termsTitle={t("settings.legal.termsTitle")}
|
|
79
|
+
termsDescription={t("settings.legal.termsDescription")}
|
|
80
|
+
eulaTitle={t("settings.legal.eulaTitle")}
|
|
81
|
+
eulaDescription={t("settings.legal.eulaDescription")}
|
|
82
|
+
onPrivacyPress={handlePrivacyPress}
|
|
83
|
+
onTermsPress={handleTermsPress}
|
|
84
|
+
onEulaPress={handleEulaPress}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
Wrapper.displayName = "LegalScreenWrapper";
|
|
88
|
+
return Wrapper;
|
|
89
|
+
}, [t, handlePrivacyPress, handleTermsPress, handleEulaPress]);
|
|
90
|
+
|
|
91
|
+
const AboutScreenWrapper = useMemo(() => {
|
|
92
|
+
const Wrapper = () => <AboutScreen config={aboutConfig} />;
|
|
93
|
+
Wrapper.displayName = "AboutScreenWrapper";
|
|
94
|
+
return Wrapper;
|
|
95
|
+
}, [aboutConfig]);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<Stack.Navigator screenOptions={screenOptions}>
|
|
99
|
+
<Stack.Screen
|
|
100
|
+
name="Settings"
|
|
101
|
+
component={SettingsScreenWrapper}
|
|
102
|
+
options={{ headerShown: false }}
|
|
103
|
+
/>
|
|
104
|
+
|
|
105
|
+
<Stack.Screen
|
|
106
|
+
name="Appearance"
|
|
107
|
+
component={AppearanceScreen}
|
|
108
|
+
options={{
|
|
109
|
+
headerShown: true,
|
|
110
|
+
headerTitle: t("settings.appearance.title"),
|
|
111
|
+
headerTitleAlign: "center",
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<Stack.Screen
|
|
116
|
+
name="About"
|
|
117
|
+
component={AboutScreenWrapper}
|
|
118
|
+
options={{
|
|
119
|
+
headerShown: true,
|
|
120
|
+
headerTitle: t("settings.about.title"),
|
|
121
|
+
headerTitleAlign: "center",
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
<Stack.Screen
|
|
126
|
+
name="Legal"
|
|
127
|
+
component={LegalScreenWrapper}
|
|
128
|
+
options={{
|
|
129
|
+
headerShown: true,
|
|
130
|
+
headerTitle: t("settings.legal.title"),
|
|
131
|
+
headerTitleAlign: "center",
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
|
|
135
|
+
{faqData && faqData.categories.length > 0 && (
|
|
136
|
+
<Stack.Screen
|
|
137
|
+
name="FAQ"
|
|
138
|
+
options={{
|
|
139
|
+
headerShown: true,
|
|
140
|
+
headerTitle: t("settings.faqs.title"),
|
|
141
|
+
headerTitleAlign: "center",
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
{() => (
|
|
145
|
+
<FAQScreen
|
|
146
|
+
categories={faqData.categories}
|
|
147
|
+
searchPlaceholder={t("settings.faqs.searchPlaceholder")}
|
|
148
|
+
emptySearchTitle={t("settings.faqs.emptySearchTitle")}
|
|
149
|
+
emptySearchMessage={t("settings.faqs.emptySearchMessage")}
|
|
150
|
+
headerTitle={t("settings.faqs.headerTitle")}
|
|
151
|
+
/>
|
|
152
|
+
)}
|
|
153
|
+
</Stack.Screen>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
{additionalScreens.map((screen) => (
|
|
157
|
+
<Stack.Screen
|
|
158
|
+
key={screen.name}
|
|
159
|
+
name={screen.name as keyof SettingsStackParamList}
|
|
160
|
+
component={screen.component}
|
|
161
|
+
options={screen.options}
|
|
162
|
+
/>
|
|
163
|
+
))}
|
|
164
|
+
</Stack.Navigator>
|
|
165
|
+
);
|
|
152
166
|
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation Handlers Hook
|
|
3
|
+
* Provides URL handlers and config generation for settings screens
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback, useMemo } from "react";
|
|
7
|
+
import { Linking } from "react-native";
|
|
8
|
+
import type { AppInfo, LegalUrls } from "../types";
|
|
9
|
+
import type { AboutConfig } from "../../../domains/about";
|
|
10
|
+
|
|
11
|
+
interface NavigationHandlersResult {
|
|
12
|
+
handlePrivacyPress: () => void;
|
|
13
|
+
handleTermsPress: () => void;
|
|
14
|
+
handleEulaPress: () => void;
|
|
15
|
+
aboutConfig: AboutConfig;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const useNavigationHandlers = (
|
|
19
|
+
appInfo: AppInfo,
|
|
20
|
+
legalUrls: LegalUrls
|
|
21
|
+
): NavigationHandlersResult => {
|
|
22
|
+
const handlePrivacyPress = useCallback(() => {
|
|
23
|
+
Linking.openURL(legalUrls.privacy);
|
|
24
|
+
}, [legalUrls.privacy]);
|
|
25
|
+
|
|
26
|
+
const handleTermsPress = useCallback(() => {
|
|
27
|
+
Linking.openURL(legalUrls.terms);
|
|
28
|
+
}, [legalUrls.terms]);
|
|
29
|
+
|
|
30
|
+
const handleEulaPress = useCallback(() => {
|
|
31
|
+
if (legalUrls.eula) {
|
|
32
|
+
Linking.openURL(legalUrls.eula);
|
|
33
|
+
}
|
|
34
|
+
}, [legalUrls.eula]);
|
|
35
|
+
|
|
36
|
+
const aboutConfig: AboutConfig = useMemo(
|
|
37
|
+
() => ({
|
|
38
|
+
appInfo: {
|
|
39
|
+
name: appInfo.name,
|
|
40
|
+
version: appInfo.version,
|
|
41
|
+
description: appInfo.description,
|
|
42
|
+
developer: appInfo.developer,
|
|
43
|
+
contactEmail: appInfo.contactEmail,
|
|
44
|
+
websiteUrl: appInfo.websiteUrl,
|
|
45
|
+
websiteDisplay: appInfo.websiteDisplay,
|
|
46
|
+
moreAppsUrl: appInfo.moreAppsUrl,
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
[appInfo]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
handlePrivacyPress,
|
|
54
|
+
handleTermsPress,
|
|
55
|
+
handleEulaPress,
|
|
56
|
+
aboutConfig,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Navigation Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { SettingsConfig, CustomSettingsSection } from "../screens/types";
|
|
6
|
+
import type { DevSettingsProps } from "../components/DevSettingsSection";
|
|
7
|
+
import type { FAQCategory } from "../../domains/faqs";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* App Info passed from main app (APP_INFO constant)
|
|
11
|
+
*/
|
|
12
|
+
export interface AppInfo {
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
developer?: string;
|
|
17
|
+
contactEmail?: string;
|
|
18
|
+
websiteUrl?: string;
|
|
19
|
+
websiteDisplay?: string;
|
|
20
|
+
moreAppsUrl?: string;
|
|
21
|
+
appStoreId?: string;
|
|
22
|
+
appStoreUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Legal URLs passed from main app (LEGAL_URLS constant)
|
|
27
|
+
*/
|
|
28
|
+
export interface LegalUrls {
|
|
29
|
+
privacy: string;
|
|
30
|
+
terms: string;
|
|
31
|
+
eula?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Navigation param list
|
|
36
|
+
*/
|
|
37
|
+
export type SettingsStackParamList = {
|
|
38
|
+
Settings: undefined;
|
|
39
|
+
Appearance: undefined;
|
|
40
|
+
About: undefined;
|
|
41
|
+
Legal: undefined;
|
|
42
|
+
FAQ: undefined;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* User profile configuration
|
|
47
|
+
*/
|
|
48
|
+
export interface UserProfileConfig {
|
|
49
|
+
displayName?: string;
|
|
50
|
+
userId?: string;
|
|
51
|
+
isAnonymous?: boolean;
|
|
52
|
+
avatarUrl?: string;
|
|
53
|
+
accountSettingsRoute?: string;
|
|
54
|
+
onPress?: () => void;
|
|
55
|
+
anonymousDisplayName?: string;
|
|
56
|
+
avatarServiceUrl?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Additional screen configuration
|
|
61
|
+
*/
|
|
62
|
+
export interface AdditionalScreen {
|
|
63
|
+
name: string;
|
|
64
|
+
component: React.ComponentType<any>;
|
|
65
|
+
options?: Record<string, any>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* FAQ Data passed from main app
|
|
70
|
+
*/
|
|
71
|
+
export interface FAQData {
|
|
72
|
+
categories: FAQCategory[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Settings Stack Navigator Props
|
|
77
|
+
*/
|
|
78
|
+
export interface SettingsStackNavigatorProps {
|
|
79
|
+
appInfo: AppInfo;
|
|
80
|
+
legalUrls: LegalUrls;
|
|
81
|
+
faqData?: FAQData;
|
|
82
|
+
config?: SettingsConfig;
|
|
83
|
+
showUserProfile?: boolean;
|
|
84
|
+
userProfile?: UserProfileConfig;
|
|
85
|
+
additionalScreens?: AdditionalScreen[];
|
|
86
|
+
devSettings?: DevSettingsProps;
|
|
87
|
+
customSections?: CustomSettingsSection[];
|
|
88
|
+
}
|