@umituz/react-native-settings 4.23.60 → 4.23.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/domains/about/presentation/components/AboutContent.tsx +2 -2
- package/src/domains/about/presentation/components/AboutHeader.tsx +2 -2
- package/src/domains/about/presentation/components/AboutSettingItem.tsx +2 -2
- package/src/domains/about/presentation/screens/AboutScreen.tsx +2 -2
- package/src/domains/about/utils/index.ts +1 -1
- package/src/domains/appearance/presentation/components/AppearanceHeader.tsx +2 -2
- package/src/domains/disclaimer/presentation/components/DisclaimerCard.tsx +4 -2
- package/src/domains/disclaimer/presentation/screens/DisclaimerScreen.tsx +2 -1
- package/src/domains/feedback/presentation/components/FeedbackModal.tsx +2 -1
- package/src/domains/feedback/presentation/components/SupportSection.tsx +9 -9
- package/src/domains/gamification/components/GamificationScreenWrapper.tsx +1 -1
- package/src/domains/legal/domain/services/UrlHandlerService.ts +1 -1
- package/src/domains/legal/presentation/screens/LegalContentScreen.tsx +2 -2
- package/src/domains/legal/presentation/screens/PrivacyPolicyScreen.tsx +2 -1
- package/src/domains/legal/presentation/screens/TermsOfServiceScreen.tsx +2 -1
- package/src/domains/localization/infrastructure/components/LanguageSwitcher.tsx +4 -4
- package/src/domains/localization/infrastructure/config/TranslationLoader.ts +4 -4
- package/src/domains/localization/infrastructure/hooks/TranslationHook.ts +3 -3
- package/src/domains/notifications/infrastructure/services/NotificationPermissions.ts +3 -3
- package/src/domains/notifications/infrastructure/services/NotificationScheduler.ts +2 -2
- package/src/domains/notifications/infrastructure/utils/dev.ts +3 -3
- package/src/domains/notifications/presentation/hooks/useTimePicker.ts +2 -2
- package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +2 -2
- package/src/domains/video-tutorials/presentation/components/VideoTutorialSection.tsx +1 -1
- package/src/domains/video-tutorials/presentation/screens/VideoTutorialsScreen.tsx +2 -1
- package/src/presentation/hooks/mutations/useSettingsMutations.ts +6 -1
- package/src/presentation/navigation/SettingsStackNavigator.tsx +7 -7
- package/src/presentation/navigation/components/wrappers/AboutScreenWrapper.tsx +2 -1
- package/src/presentation/navigation/components/wrappers/SettingsScreenWrapper.tsx +7 -4
- package/src/presentation/navigation/utils/navigationTranslations.ts +2 -2
- package/src/presentation/screens/components/GamificationSettingsItem.tsx +4 -2
- package/src/presentation/screens/components/SettingsContent.tsx +13 -4
- package/src/presentation/screens/components/SubscriptionSettingsItem.tsx +2 -1
- package/src/presentation/screens/components/WalletSettingsItem.tsx +2 -1
- package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +1 -1
- package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +10 -6
- package/src/presentation/screens/hooks/useFeatureDetection.ts +3 -3
- package/src/presentation/screens/types/UserFeatureConfig.ts +3 -2
- package/src/presentation/screens/components/index.ts +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-settings",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.61",
|
|
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",
|
|
@@ -7,7 +7,7 @@ import { View, StyleSheet } from 'react-native';
|
|
|
7
7
|
import { AboutSettingItem } from './AboutSettingItem';
|
|
8
8
|
import { AppInfo } from '../../domain/entities/AppInfo';
|
|
9
9
|
import { AboutConfig } from '../../domain/entities/AppInfo';
|
|
10
|
-
import { useAppDesignTokens, AtomicText } from '@umituz/react-native-design-system';
|
|
10
|
+
import { useAppDesignTokens, AtomicText, type DesignTokens } from '@umituz/react-native-design-system';
|
|
11
11
|
|
|
12
12
|
export interface AboutContentProps {
|
|
13
13
|
/** App information to display */
|
|
@@ -88,7 +88,7 @@ export const AboutContent: React.FC<AboutContentProps> = ({
|
|
|
88
88
|
);
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
const getStyles = (tokens:
|
|
91
|
+
const getStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
92
92
|
content: {
|
|
93
93
|
paddingVertical: 8,
|
|
94
94
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { View, StyleSheet, ViewStyle, TextStyle } from 'react-native';
|
|
7
|
-
import { useAppDesignTokens, AtomicText } from '@umituz/react-native-design-system';
|
|
7
|
+
import { useAppDesignTokens, AtomicText, type DesignTokens } from '@umituz/react-native-design-system';
|
|
8
8
|
import { AppInfo } from '../../domain/entities/AppInfo';
|
|
9
9
|
|
|
10
10
|
export interface AboutHeaderProps {
|
|
@@ -54,7 +54,7 @@ export const AboutHeader: React.FC<AboutHeaderProps> = ({
|
|
|
54
54
|
);
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
const getStyles = (tokens:
|
|
57
|
+
const getStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
58
58
|
header: {
|
|
59
59
|
alignItems: 'center',
|
|
60
60
|
paddingVertical: 24,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ViewStyle,
|
|
13
13
|
TextStyle,
|
|
14
14
|
} from 'react-native';
|
|
15
|
-
import { useAppDesignTokens, AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
15
|
+
import { useAppDesignTokens, AtomicText, AtomicIcon, type DesignTokens } from '@umituz/react-native-design-system';
|
|
16
16
|
|
|
17
17
|
export interface AboutSettingItemProps {
|
|
18
18
|
/** Icon component (any React component) */
|
|
@@ -123,7 +123,7 @@ export const AboutSettingItem: React.FC<AboutSettingItemProps> = memo(({
|
|
|
123
123
|
);
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
const getStyles = (tokens:
|
|
126
|
+
const getStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
127
127
|
container: {
|
|
128
128
|
flexDirection: 'row',
|
|
129
129
|
alignItems: 'center',
|
|
@@ -38,7 +38,7 @@ export interface AboutScreenProps {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
import { useAboutInfo } from '../hooks/useAboutInfo';
|
|
41
|
-
import { useAppDesignTokens, AtomicText } from '@umituz/react-native-design-system';
|
|
41
|
+
import { useAppDesignTokens, AtomicText, type DesignTokens } from '@umituz/react-native-design-system';
|
|
42
42
|
|
|
43
43
|
export const AboutScreen: React.FC<AboutScreenProps> = ({
|
|
44
44
|
config,
|
|
@@ -174,7 +174,7 @@ export const AboutScreen: React.FC<AboutScreenProps> = ({
|
|
|
174
174
|
);
|
|
175
175
|
};
|
|
176
176
|
|
|
177
|
-
const getStyles = (tokens:
|
|
177
|
+
const getStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
178
178
|
container: {
|
|
179
179
|
flex: 1,
|
|
180
180
|
},
|
|
@@ -107,7 +107,7 @@ export const isValidUrl = (url: string): boolean => {
|
|
|
107
107
|
try {
|
|
108
108
|
const urlObj = new URL(url);
|
|
109
109
|
// Only allow http and https protocols
|
|
110
|
-
return
|
|
110
|
+
return urlObj.protocol === 'http:' || urlObj.protocol === 'https:';
|
|
111
111
|
} catch {
|
|
112
112
|
return false;
|
|
113
113
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { View, StyleSheet } from "react-native";
|
|
7
|
+
import { View, StyleSheet, type StyleProp, type ViewStyle } from "react-native";
|
|
8
8
|
import { AtomicText } from "@umituz/react-native-design-system";
|
|
9
9
|
import type { DesignTokens } from "@umituz/react-native-design-system";
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ export interface AppearanceHeaderProps {
|
|
|
16
16
|
subtitleType?: "bodyLarge" | "bodyMedium" | "bodySmall";
|
|
17
17
|
titleColor?: "primary" | "secondary" | "tertiary";
|
|
18
18
|
subtitleColor?: "primary" | "secondary" | "tertiary";
|
|
19
|
-
style?:
|
|
19
|
+
style?: StyleProp<ViewStyle>;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const AppearanceHeader: React.FC<AppearanceHeaderProps> = ({
|
|
@@ -8,10 +8,12 @@ import {
|
|
|
8
8
|
View,
|
|
9
9
|
StyleSheet,
|
|
10
10
|
TouchableOpacity,
|
|
11
|
+
type TextStyle,
|
|
11
12
|
} from 'react-native';
|
|
12
13
|
|
|
13
14
|
import { useAppDesignTokens, withAlpha } from '@umituz/react-native-design-system';
|
|
14
15
|
import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
16
|
+
import type { IconName } from '@umituz/react-native-design-system';
|
|
15
17
|
|
|
16
18
|
export interface DisclaimerCardProps {
|
|
17
19
|
title: string;
|
|
@@ -55,7 +57,7 @@ export const DisclaimerCard: React.FC<DisclaimerCardProps> = ({
|
|
|
55
57
|
},
|
|
56
58
|
]}
|
|
57
59
|
>
|
|
58
|
-
<AtomicIcon name={iconName as
|
|
60
|
+
<AtomicIcon name={iconName as IconName} color="warning" />
|
|
59
61
|
</View>
|
|
60
62
|
<AtomicText type="bodyLarge" color="primary" style={styles.title}>
|
|
61
63
|
{title}
|
|
@@ -103,7 +105,7 @@ const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
|
|
|
103
105
|
|
|
104
106
|
title: {
|
|
105
107
|
flex: 1,
|
|
106
|
-
fontWeight: tokens.typography.labelLarge.fontWeight as
|
|
108
|
+
fontWeight: tokens.typography.labelLarge.fontWeight as TextStyle['fontWeight'],
|
|
107
109
|
fontSize: tokens.typography.labelLarge.fontSize,
|
|
108
110
|
},
|
|
109
111
|
|
|
@@ -18,6 +18,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
|
|
|
18
18
|
|
|
19
19
|
import { useAppDesignTokens, withAlpha } from '@umituz/react-native-design-system';
|
|
20
20
|
import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
21
|
+
import type { IconName } from '@umituz/react-native-design-system';
|
|
21
22
|
import { useLocalization } from '../../../localization';
|
|
22
23
|
|
|
23
24
|
export interface DisclaimerScreenProps {
|
|
@@ -68,7 +69,7 @@ export const DisclaimerScreen: React.FC<DisclaimerScreenProps> = ({
|
|
|
68
69
|
},
|
|
69
70
|
]}
|
|
70
71
|
>
|
|
71
|
-
<AtomicIcon name={iconName as
|
|
72
|
+
<AtomicIcon name={iconName as IconName} color="warning" size="xl" />
|
|
72
73
|
</View>
|
|
73
74
|
</View>
|
|
74
75
|
|
|
@@ -9,6 +9,7 @@ import { SafeAreaView } from "react-native-safe-area-context";
|
|
|
9
9
|
import { useAppDesignTokens, AtomicText, AtomicIcon, BaseModal } from "@umituz/react-native-design-system";
|
|
10
10
|
import { FeedbackForm } from "./FeedbackForm";
|
|
11
11
|
import type { FeedbackType, FeedbackRating } from "../../domain/entities/FeedbackEntity";
|
|
12
|
+
import type { FeedbackFormProps } from "./FeedbackForm";
|
|
12
13
|
|
|
13
14
|
export interface FeedbackModalProps {
|
|
14
15
|
visible: boolean;
|
|
@@ -18,7 +19,7 @@ export interface FeedbackModalProps {
|
|
|
18
19
|
isSubmitting?: boolean;
|
|
19
20
|
title?: string;
|
|
20
21
|
subtitle?: string;
|
|
21
|
-
texts:
|
|
22
|
+
texts: FeedbackFormProps['texts'];
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const FeedbackModal: React.FC<FeedbackModalProps> = ({
|
|
@@ -14,7 +14,7 @@ export interface FeedbackConfig {
|
|
|
14
14
|
title?: string;
|
|
15
15
|
description?: string;
|
|
16
16
|
initialType?: FeedbackType;
|
|
17
|
-
onSubmit?: (data: { type:
|
|
17
|
+
onSubmit?: (data: { type: FeedbackType; rating: number; description: string; title: string }) => Promise<void>;
|
|
18
18
|
onPress?: () => void;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -28,12 +28,12 @@ export interface RatingConfig {
|
|
|
28
28
|
|
|
29
29
|
export interface FeedbackModalTexts {
|
|
30
30
|
title?: string;
|
|
31
|
-
ratingLabel
|
|
32
|
-
descriptionPlaceholder
|
|
33
|
-
submitButton
|
|
34
|
-
submittingButton
|
|
35
|
-
feedbackTypes
|
|
36
|
-
defaultTitle
|
|
31
|
+
ratingLabel: string;
|
|
32
|
+
descriptionPlaceholder: string;
|
|
33
|
+
submitButton: string;
|
|
34
|
+
submittingButton: string;
|
|
35
|
+
feedbackTypes: Array<{ type: FeedbackType; label: string }>;
|
|
36
|
+
defaultTitle: (type: FeedbackType) => string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export interface SupportSectionProps {
|
|
@@ -42,7 +42,7 @@ export interface SupportSectionProps {
|
|
|
42
42
|
renderSection: (props: { title: string; children: React.ReactNode }) => React.ReactElement | null;
|
|
43
43
|
renderItem: (props: {
|
|
44
44
|
title: string;
|
|
45
|
-
icon:
|
|
45
|
+
icon: string;
|
|
46
46
|
onPress: () => void;
|
|
47
47
|
isLast?: boolean
|
|
48
48
|
}) => React.ReactElement | null;
|
|
@@ -60,7 +60,7 @@ export const SupportSection: React.FC<SupportSectionProps> = ({
|
|
|
60
60
|
const [modalVisible, setModalVisible] = useState(false);
|
|
61
61
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
62
62
|
|
|
63
|
-
const handleFeedbackSubmit = async (data: { type:
|
|
63
|
+
const handleFeedbackSubmit = async (data: { type: FeedbackType; rating: number; description: string; title: string }) => {
|
|
64
64
|
if (feedbackConfig.config?.onSubmit) {
|
|
65
65
|
setIsSubmitting(true);
|
|
66
66
|
try {
|
|
@@ -18,7 +18,7 @@ export const GamificationScreenWrapper: React.FC<GamificationScreenWrapperProps>
|
|
|
18
18
|
} = useGamification(config);
|
|
19
19
|
|
|
20
20
|
// Transform store achievements to UI props
|
|
21
|
-
const achievementItems:
|
|
21
|
+
const achievementItems: AchievementItemProps[] = achievements.map(a => ({
|
|
22
22
|
...a,
|
|
23
23
|
title: a.title,
|
|
24
24
|
description: a.description,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { View, ScrollView, StyleSheet } from "react-native";
|
|
7
7
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
8
|
-
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
8
|
+
import { useAppDesignTokens, type DesignTokens } from "@umituz/react-native-design-system";
|
|
9
9
|
import { AtomicText, AtomicButton } from "@umituz/react-native-design-system";
|
|
10
10
|
import { UrlHandlerService } from "../../domain/services/UrlHandlerService";
|
|
11
11
|
import { ContentValidationService } from "../../domain/services/ContentValidationService";
|
|
@@ -20,7 +20,7 @@ export interface LegalContentScreenProps {
|
|
|
20
20
|
onUrlPress?: () => void;
|
|
21
21
|
testID?: string;
|
|
22
22
|
styleCacheKey: string;
|
|
23
|
-
createStyles: (tokens:
|
|
23
|
+
createStyles: (tokens: DesignTokens) => ReturnType<typeof StyleSheet.create>;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export const LegalContentScreen: React.FC<LegalContentScreenProps> = React.memo(({
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { StyleSheet } from "react-native";
|
|
7
|
+
import type { DesignTokens } from "@umituz/react-native-design-system";
|
|
7
8
|
import { LegalContentScreen } from "./LegalContentScreen";
|
|
8
9
|
|
|
9
10
|
export interface PrivacyPolicyScreenProps {
|
|
@@ -25,7 +26,7 @@ export const PrivacyPolicyScreen: React.FC<PrivacyPolicyScreenProps> = React.mem
|
|
|
25
26
|
onUrlPress,
|
|
26
27
|
testID = "privacy-policy-screen",
|
|
27
28
|
}) => {
|
|
28
|
-
const createStyles = (tokens:
|
|
29
|
+
const createStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
29
30
|
container: {
|
|
30
31
|
flex: 1,
|
|
31
32
|
},
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { StyleSheet } from "react-native";
|
|
7
|
+
import type { DesignTokens } from "@umituz/react-native-design-system";
|
|
7
8
|
import { LegalContentScreen } from "./LegalContentScreen";
|
|
8
9
|
|
|
9
10
|
export interface TermsOfServiceScreenProps {
|
|
@@ -25,7 +26,7 @@ export const TermsOfServiceScreen: React.FC<TermsOfServiceScreenProps> = React.m
|
|
|
25
26
|
onUrlPress,
|
|
26
27
|
testID = "terms-of-service-screen",
|
|
27
28
|
}) => {
|
|
28
|
-
const createStyles = (tokens:
|
|
29
|
+
const createStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
29
30
|
container: {
|
|
30
31
|
flex: 1,
|
|
31
32
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React, { useMemo } from 'react';
|
|
7
|
-
import { TouchableOpacity } from 'react-native';
|
|
7
|
+
import { TouchableOpacity, type StyleProp, type ViewStyle, type TextStyle } from 'react-native';
|
|
8
8
|
// @ts-ignore - Optional peer dependency
|
|
9
9
|
import { useAppDesignTokens, AtomicText } from '@umituz/react-native-design-system';
|
|
10
10
|
import { useLanguageSwitcher } from './useLanguageSwitcher';
|
|
@@ -15,9 +15,9 @@ export interface LanguageSwitcherProps {
|
|
|
15
15
|
showFlag?: boolean;
|
|
16
16
|
color?: string;
|
|
17
17
|
onPress?: () => void;
|
|
18
|
-
style?:
|
|
19
|
-
textStyle?:
|
|
20
|
-
iconStyle?:
|
|
18
|
+
style?: StyleProp<ViewStyle>;
|
|
19
|
+
textStyle?: StyleProp<TextStyle>;
|
|
20
|
+
iconStyle?: StyleProp<TextStyle>;
|
|
21
21
|
testID?: string;
|
|
22
22
|
disabled?: boolean;
|
|
23
23
|
accessibilityLabel?: string;
|
|
@@ -8,7 +8,7 @@ export class TranslationLoader {
|
|
|
8
8
|
/**
|
|
9
9
|
* Load package translations (empty by default - apps provide their own)
|
|
10
10
|
*/
|
|
11
|
-
static loadPackageTranslations(): Record<string,
|
|
11
|
+
static loadPackageTranslations(): Record<string, Record<string, unknown>> {
|
|
12
12
|
// Package doesn't include any translations by default
|
|
13
13
|
// Consuming applications should provide their own translations
|
|
14
14
|
return { 'en-US': {} };
|
|
@@ -17,7 +17,7 @@ export class TranslationLoader {
|
|
|
17
17
|
/**
|
|
18
18
|
* Deep merge translations (override wins)
|
|
19
19
|
*/
|
|
20
|
-
static mergeTranslations(base:
|
|
20
|
+
static mergeTranslations(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown> {
|
|
21
21
|
if (!override || Object.keys(override).length === 0) {
|
|
22
22
|
return base;
|
|
23
23
|
}
|
|
@@ -30,7 +30,7 @@ export class TranslationLoader {
|
|
|
30
30
|
const overrideVal = override[key];
|
|
31
31
|
|
|
32
32
|
if (this.isObject(baseVal) && this.isObject(overrideVal)) {
|
|
33
|
-
merged[key] = this.mergeTranslations(baseVal, overrideVal);
|
|
33
|
+
merged[key] = this.mergeTranslations(baseVal as Record<string, unknown>, overrideVal as Record<string, unknown>);
|
|
34
34
|
} else {
|
|
35
35
|
merged[key] = overrideVal;
|
|
36
36
|
}
|
|
@@ -40,7 +40,7 @@ export class TranslationLoader {
|
|
|
40
40
|
return merged;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
private static isObject(val:
|
|
43
|
+
private static isObject(val: unknown): boolean {
|
|
44
44
|
return val !== null && typeof val === 'object' && !Array.isArray(val);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -14,18 +14,18 @@ export class TranslationHook {
|
|
|
14
14
|
/**
|
|
15
15
|
* Get translation function with proper fallbacks
|
|
16
16
|
*/
|
|
17
|
-
static useTranslationFunction(): (key: string, options?:
|
|
17
|
+
static useTranslationFunction(): (key: string, options?: Record<string, unknown>) => string {
|
|
18
18
|
// Always call useTranslation hook (React hooks rules)
|
|
19
19
|
const translationResult = useTranslation(undefined, { i18n });
|
|
20
20
|
|
|
21
21
|
// Use react-i18next if available, otherwise fallback to direct i18n
|
|
22
22
|
if (translationResult?.t && typeof translationResult.t === 'function' && i18n.isInitialized) {
|
|
23
|
-
return (key: string, options?:
|
|
23
|
+
return (key: string, options?: Record<string, unknown>): string => {
|
|
24
24
|
const result = translationResult.t(key, options);
|
|
25
25
|
return typeof result === 'string' ? result : String(result);
|
|
26
26
|
};
|
|
27
27
|
} else {
|
|
28
|
-
return (key: string, options?:
|
|
28
|
+
return (key: string, options?: Record<string, unknown>): string => {
|
|
29
29
|
// Fallback to direct i18n.t
|
|
30
30
|
if (i18n.isInitialized && typeof i18n.t === 'function') {
|
|
31
31
|
const result = i18n.t(key, options);
|
|
@@ -12,12 +12,12 @@ export class NotificationPermissions {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const permissionsResponse = await Notifications.getPermissionsAsync();
|
|
15
|
-
const existingStatus =
|
|
15
|
+
const existingStatus = permissionsResponse.status || (permissionsResponse.granted ? 'granted' : 'denied');
|
|
16
16
|
let finalStatus = existingStatus;
|
|
17
17
|
|
|
18
18
|
if (existingStatus !== 'granted') {
|
|
19
19
|
const requestResponse = await Notifications.requestPermissionsAsync();
|
|
20
|
-
finalStatus =
|
|
20
|
+
finalStatus = requestResponse.status || (requestResponse.granted ? 'granted' : 'denied');
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (Platform.OS === 'android') {
|
|
@@ -35,7 +35,7 @@ export class NotificationPermissions {
|
|
|
35
35
|
try {
|
|
36
36
|
if (!Device.isDevice) return false;
|
|
37
37
|
const permissionsResponse = await Notifications.getPermissionsAsync();
|
|
38
|
-
return
|
|
38
|
+
return permissionsResponse.status === 'granted' || permissionsResponse.granted === true;
|
|
39
39
|
} catch (error) {
|
|
40
40
|
devError('[NotificationPermissions] Permission check failed:', error);
|
|
41
41
|
return false;
|
|
@@ -5,7 +5,7 @@ export class NotificationScheduler {
|
|
|
5
5
|
async scheduleNotification(options: ScheduleNotificationOptions): Promise<string> {
|
|
6
6
|
const { title, body, data = {}, trigger, sound = true, badge, categoryIdentifier } = options;
|
|
7
7
|
|
|
8
|
-
let notificationTrigger:
|
|
8
|
+
let notificationTrigger: Notifications.NotificationTriggerInput = null;
|
|
9
9
|
|
|
10
10
|
if (trigger.type === 'date') {
|
|
11
11
|
notificationTrigger = {
|
|
@@ -69,7 +69,7 @@ export class NotificationScheduler {
|
|
|
69
69
|
content: {
|
|
70
70
|
title: notification.content.title || '',
|
|
71
71
|
body: notification.content.body || '',
|
|
72
|
-
data: notification.content.data as Record<string,
|
|
72
|
+
data: notification.content.data as Record<string, unknown>,
|
|
73
73
|
},
|
|
74
74
|
trigger: notification.trigger,
|
|
75
75
|
}));
|
|
@@ -6,19 +6,19 @@ export const isDev = () => {
|
|
|
6
6
|
}
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export const devLog = (message: string, ...args:
|
|
9
|
+
export const devLog = (message: string, ...args: unknown[]) => {
|
|
10
10
|
if (isDev()) {
|
|
11
11
|
console.log(message, ...args);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
export const devError = (message: string, ...args:
|
|
15
|
+
export const devError = (message: string, ...args: unknown[]) => {
|
|
16
16
|
if (isDev()) {
|
|
17
17
|
console.error(message, ...args);
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
export const devWarn = (message: string, ...args:
|
|
21
|
+
export const devWarn = (message: string, ...args: unknown[]) => {
|
|
22
22
|
if (isDev()) {
|
|
23
23
|
console.warn(message, ...args);
|
|
24
24
|
}
|
|
@@ -18,7 +18,7 @@ export interface TimePickerHandlers {
|
|
|
18
18
|
pickerMode: PickerMode;
|
|
19
19
|
handleStartTimePress: () => void;
|
|
20
20
|
handleEndTimePress: () => void;
|
|
21
|
-
handleTimeChange: (event:
|
|
21
|
+
handleTimeChange: (event: { type: string }, selectedDate?: Date) => void;
|
|
22
22
|
getPickerDate: () => Date;
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -37,7 +37,7 @@ export const useTimePicker = ({
|
|
|
37
37
|
setPickerMode('end');
|
|
38
38
|
}, []);
|
|
39
39
|
|
|
40
|
-
const handleTimeChange = useCallback((event:
|
|
40
|
+
const handleTimeChange = useCallback((event: { type: string }, selectedDate?: Date) => {
|
|
41
41
|
if (event.type === 'set' && selectedDate) {
|
|
42
42
|
const hours = selectedDate.getHours();
|
|
43
43
|
const minutes = selectedDate.getMinutes();
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import React, { useMemo } from 'react';
|
|
9
9
|
import { View, StyleSheet } from 'react-native';
|
|
10
|
-
import { AtomicIcon, AtomicCard, AtomicText, ScreenLayout, STATIC_TOKENS, AtomicSpinner } from '@umituz/react-native-design-system';
|
|
10
|
+
import { AtomicIcon, AtomicCard, AtomicText, ScreenLayout, STATIC_TOKENS, AtomicSpinner, type IconColor } from '@umituz/react-native-design-system';
|
|
11
11
|
import { Switch } from 'react-native';
|
|
12
12
|
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
13
13
|
import { useNotificationSettings } from '../../infrastructure/hooks/useNotificationSettings';
|
|
@@ -52,7 +52,7 @@ export const NotificationsScreen: React.FC<NotificationsScreenProps> = ({
|
|
|
52
52
|
<AtomicCard style={styles.card}>
|
|
53
53
|
<View style={styles.settingItem}>
|
|
54
54
|
<View style={styles.iconContainer}>
|
|
55
|
-
<AtomicIcon name={iconName} size="lg" color={iconColor as
|
|
55
|
+
<AtomicIcon name={iconName} size="lg" color={iconColor as IconColor} />
|
|
56
56
|
</View>
|
|
57
57
|
<View style={styles.textContainer}>
|
|
58
58
|
<AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
AtomicSpinner,
|
|
20
20
|
AtomicText,
|
|
21
21
|
} from "@umituz/react-native-design-system";
|
|
22
|
+
import type { DesignTokens } from "@umituz/react-native-design-system";
|
|
22
23
|
import type { VideoTutorial } from "../../types";
|
|
23
24
|
import { VideoTutorialCard } from "../components/VideoTutorialCard";
|
|
24
25
|
|
|
@@ -114,7 +115,7 @@ export const VideoTutorialsScreen: React.FC<VideoTutorialsScreenProps> = React.m
|
|
|
114
115
|
}
|
|
115
116
|
);
|
|
116
117
|
|
|
117
|
-
const getStyles = (tokens:
|
|
118
|
+
const getStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
118
119
|
title: {
|
|
119
120
|
fontSize: tokens.typography.headlineLarge.fontSize,
|
|
120
121
|
color: tokens.colors.textPrimary,
|
|
@@ -49,7 +49,12 @@ export const useResetSettingsMutation = (userId: string) => {
|
|
|
49
49
|
if (!result.success) {
|
|
50
50
|
throw new Error(result.error?.message || 'Failed to reset settings');
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
// After reset, fetch fresh settings (defaults)
|
|
53
|
+
const freshResult = await settingsService.getSettings(userId);
|
|
54
|
+
if (!freshResult.success || !freshResult.data) {
|
|
55
|
+
throw new Error('Failed to fetch settings after reset');
|
|
56
|
+
}
|
|
57
|
+
return freshResult.data;
|
|
53
58
|
},
|
|
54
59
|
onSuccess: (data) => {
|
|
55
60
|
queryClient.setQueryData([...SETTINGS_QUERY_KEY, userId], data);
|
|
@@ -74,7 +74,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
|
|
|
74
74
|
);
|
|
75
75
|
|
|
76
76
|
const screens = React.useMemo(() => {
|
|
77
|
-
const list: StackScreen
|
|
77
|
+
const list: StackScreen[] = [
|
|
78
78
|
{
|
|
79
79
|
name: "SettingsMain",
|
|
80
80
|
options: { headerShown: false },
|
|
@@ -138,10 +138,10 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
|
|
|
138
138
|
// Add additional screens
|
|
139
139
|
additionalScreens.forEach((screen) => {
|
|
140
140
|
list.push({
|
|
141
|
-
name: screen.name
|
|
142
|
-
component: screen.component as
|
|
143
|
-
children: screen.children as
|
|
144
|
-
options: screen.options as
|
|
141
|
+
name: screen.name,
|
|
142
|
+
component: screen.component as StackScreen['component'],
|
|
143
|
+
children: screen.children as StackScreen['children'],
|
|
144
|
+
options: screen.options as StackScreen['options'],
|
|
145
145
|
});
|
|
146
146
|
});
|
|
147
147
|
|
|
@@ -196,9 +196,9 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
|
|
|
196
196
|
|
|
197
197
|
const navigatorConfig: StackNavigatorConfig<SettingsStackParamList> = {
|
|
198
198
|
initialRouteName: "SettingsMain",
|
|
199
|
-
screenOptions
|
|
199
|
+
screenOptions,
|
|
200
200
|
screens,
|
|
201
201
|
};
|
|
202
202
|
|
|
203
|
-
return <StackNavigator<
|
|
203
|
+
return <StackNavigator<SettingsStackParamList> config={navigatorConfig} />;
|
|
204
204
|
};
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import React from "react";
|
|
5
5
|
import { AboutScreen } from "../../../../domains/about";
|
|
6
|
+
import type { AboutConfig } from "../../../../domains/about/domain/entities/AppInfo";
|
|
6
7
|
|
|
7
8
|
export interface AboutScreenWrapperProps {
|
|
8
|
-
config:
|
|
9
|
+
config: AboutConfig;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export const AboutScreenWrapper: React.FC<AboutScreenWrapperProps> = ({
|
|
@@ -3,14 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import React from "react";
|
|
5
5
|
import { SettingsScreen } from "../../../screens/SettingsScreen";
|
|
6
|
+
import type { SettingsConfig, CustomSettingsSection } from "../../../screens/types";
|
|
7
|
+
import type { UserProfileConfig } from "../../types";
|
|
8
|
+
import type { DevSettingsProps } from "../../../../domains/dev";
|
|
6
9
|
|
|
7
10
|
export interface SettingsScreenWrapperProps {
|
|
8
|
-
config
|
|
11
|
+
config?: SettingsConfig;
|
|
9
12
|
appVersion: string;
|
|
10
13
|
showUserProfile: boolean;
|
|
11
|
-
userProfile
|
|
12
|
-
devSettings
|
|
13
|
-
customSections
|
|
14
|
+
userProfile?: UserProfileConfig;
|
|
15
|
+
devSettings?: DevSettingsProps;
|
|
16
|
+
customSections?: CustomSettingsSection[];
|
|
14
17
|
showHeader?: boolean;
|
|
15
18
|
showCloseButton?: boolean;
|
|
16
19
|
onClose?: () => void;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Creates translation objects for navigation screens
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export const createNotificationTranslations = (t:
|
|
6
|
+
export const createNotificationTranslations = (t: (key: string) => string) => ({
|
|
7
7
|
screenTitle: t("settings.notifications.title"),
|
|
8
8
|
masterToggleTitle: t("settings.notifications.masterToggleTitle"),
|
|
9
9
|
masterToggleDescription: t("settings.notifications.masterToggleDescription"),
|
|
@@ -17,7 +17,7 @@ export const createNotificationTranslations = (t: any) => ({
|
|
|
17
17
|
quietHoursDescription: t("settings.notifications.quietHoursDescription"),
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
export const createQuietHoursTranslations = (t:
|
|
20
|
+
export const createQuietHoursTranslations = (t: (key: string) => string) => ({
|
|
21
21
|
title: t("settings.notifications.quietHours.title"),
|
|
22
22
|
description: t("settings.notifications.quietHours.description"),
|
|
23
23
|
startTimeLabel: t("settings.notifications.quietHours.startTimeLabel"),
|
|
@@ -4,10 +4,12 @@ import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
|
4
4
|
import type { IconName } from "@umituz/react-native-design-system";
|
|
5
5
|
import { SettingsItemCard } from "../../components/SettingsItemCard";
|
|
6
6
|
import { useGamification } from "../../../domains/gamification";
|
|
7
|
+
import type { GamificationItemConfig } from "../../screens/types/UserFeatureConfig";
|
|
8
|
+
import type { GamificationSettingsConfig } from "../../../domains/gamification";
|
|
7
9
|
|
|
8
10
|
export interface GamificationSettingsItemProps {
|
|
9
|
-
config:
|
|
10
|
-
gamificationConfig?:
|
|
11
|
+
config: GamificationItemConfig;
|
|
12
|
+
gamificationConfig?: GamificationSettingsConfig;
|
|
11
13
|
t: (key: string) => string;
|
|
12
14
|
}
|
|
13
15
|
|
|
@@ -20,7 +20,6 @@ import { GamificationSettingsItem } from "./GamificationSettingsItem";
|
|
|
20
20
|
|
|
21
21
|
interface SettingsContentProps {
|
|
22
22
|
normalizedConfig: NormalizedConfig;
|
|
23
|
-
config?: any;
|
|
24
23
|
features: {
|
|
25
24
|
appearance: boolean;
|
|
26
25
|
language: boolean;
|
|
@@ -37,19 +36,28 @@ interface SettingsContentProps {
|
|
|
37
36
|
gamification: boolean;
|
|
38
37
|
};
|
|
39
38
|
showUserProfile?: boolean;
|
|
40
|
-
userProfile?:
|
|
39
|
+
userProfile?: {
|
|
40
|
+
displayName?: string;
|
|
41
|
+
userId?: string;
|
|
42
|
+
isAnonymous?: boolean;
|
|
43
|
+
avatarUrl?: string;
|
|
44
|
+
accountSettingsRoute?: string;
|
|
45
|
+
onPress?: () => void;
|
|
46
|
+
anonymousDisplayName?: string;
|
|
47
|
+
avatarServiceUrl?: string;
|
|
48
|
+
};
|
|
41
49
|
showFooter?: boolean;
|
|
42
50
|
footerText?: string;
|
|
43
51
|
appVersion?: string;
|
|
44
52
|
customSections?: CustomSettingsSection[];
|
|
45
53
|
showCloseButton?: boolean;
|
|
54
|
+
emptyStateText?: string;
|
|
46
55
|
devSettings?: DevSettingsProps;
|
|
47
56
|
gamificationConfig?: import("../../../domains/gamification").GamificationSettingsConfig;
|
|
48
57
|
}
|
|
49
58
|
|
|
50
59
|
export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
51
60
|
normalizedConfig,
|
|
52
|
-
config,
|
|
53
61
|
features,
|
|
54
62
|
showUserProfile = false,
|
|
55
63
|
userProfile,
|
|
@@ -58,6 +66,7 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
|
58
66
|
appVersion,
|
|
59
67
|
customSections = [],
|
|
60
68
|
showCloseButton = false,
|
|
69
|
+
emptyStateText,
|
|
61
70
|
devSettings,
|
|
62
71
|
gamificationConfig,
|
|
63
72
|
}) => {
|
|
@@ -115,7 +124,7 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
|
115
124
|
|
|
116
125
|
{!hasAnyFeatures && (
|
|
117
126
|
<View style={styles.emptyContainer}>
|
|
118
|
-
<SettingsSection title={
|
|
127
|
+
<SettingsSection title={emptyStateText || t("settings.noOptionsAvailable")}>
|
|
119
128
|
<View />
|
|
120
129
|
</SettingsSection>
|
|
121
130
|
</View>
|
|
@@ -3,9 +3,10 @@ import React from "react";
|
|
|
3
3
|
import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
4
4
|
import { SettingsItemCard } from "../../components/SettingsItemCard";
|
|
5
5
|
import type { IconName } from "@umituz/react-native-design-system";
|
|
6
|
+
import type { SubscriptionConfig } from "../../screens/types/UserFeatureConfig";
|
|
6
7
|
|
|
7
8
|
export interface SubscriptionSettingsItemProps {
|
|
8
|
-
config:
|
|
9
|
+
config: SubscriptionConfig;
|
|
9
10
|
t: (key: string) => string;
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -3,9 +3,10 @@ import React from "react";
|
|
|
3
3
|
import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
4
4
|
import { SettingsItemCard } from "../../components/SettingsItemCard";
|
|
5
5
|
import type { IconName } from "@umituz/react-native-design-system";
|
|
6
|
+
import type { WalletConfig } from "../../screens/types/UserFeatureConfig";
|
|
6
7
|
|
|
7
8
|
export interface WalletSettingsItemProps {
|
|
8
|
-
config:
|
|
9
|
+
config: WalletConfig;
|
|
9
10
|
t: (key: string) => string;
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -8,7 +8,7 @@ import { SettingsSection } from "../../../components/SettingsSection";
|
|
|
8
8
|
|
|
9
9
|
interface IdentitySettingsSectionProps {
|
|
10
10
|
normalizedConfig: NormalizedConfig;
|
|
11
|
-
features:
|
|
11
|
+
features: { about: boolean; legal: boolean; [key: string]: boolean };
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export const IdentitySettingsSection: React.FC<IdentitySettingsSectionProps> = ({
|
|
@@ -4,10 +4,11 @@ import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
|
4
4
|
import { SupportSection } from "../../../../domains/feedback/presentation/components/SupportSection";
|
|
5
5
|
import { SettingsSection } from "../../../components/SettingsSection";
|
|
6
6
|
import { SettingsItemCard } from "../../../components/SettingsItemCard";
|
|
7
|
+
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|
|
7
8
|
|
|
8
9
|
interface SupportSettingsSectionProps {
|
|
9
|
-
features:
|
|
10
|
-
normalizedConfig:
|
|
10
|
+
features: { feedback: boolean; rating: boolean; faqs: boolean; [key: string]: boolean };
|
|
11
|
+
normalizedConfig: NormalizedConfig;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export const SupportSettingsSection: React.FC<SupportSettingsSectionProps> = ({
|
|
@@ -27,8 +28,8 @@ export const SupportSettingsSection: React.FC<SupportSettingsSectionProps> = ({
|
|
|
27
28
|
<SettingsSection title={t("settings.support.title")}>
|
|
28
29
|
{(features.feedback || features.rating) && (
|
|
29
30
|
<SupportSection
|
|
30
|
-
renderSection={(props:
|
|
31
|
-
renderItem={(props:
|
|
31
|
+
renderSection={(props: { title: string; children: React.ReactNode }) => <>{props.children}</>}
|
|
32
|
+
renderItem={(props: { title: string; icon: string; onPress: () => void; isLast?: boolean }) => (
|
|
32
33
|
<SettingsItemCard
|
|
33
34
|
title={props.title}
|
|
34
35
|
icon={props.icon}
|
|
@@ -40,17 +41,20 @@ export const SupportSettingsSection: React.FC<SupportSettingsSectionProps> = ({
|
|
|
40
41
|
feedbackConfig={{
|
|
41
42
|
enabled: features.feedback,
|
|
42
43
|
config: {
|
|
43
|
-
...normalizedConfig.feedback.config,
|
|
44
44
|
title: normalizedConfig.feedback.config?.title || t("settings.feedback.title"),
|
|
45
45
|
description: normalizedConfig.feedback.config?.description || t("settings.feedback.description"),
|
|
46
|
+
initialType: normalizedConfig.feedback.config?.initialType,
|
|
47
|
+
onSubmit: normalizedConfig.feedback.config?.onSubmit,
|
|
48
|
+
onPress: normalizedConfig.feedback.config?.onPress,
|
|
46
49
|
}
|
|
47
50
|
}}
|
|
48
51
|
ratingConfig={{
|
|
49
52
|
enabled: features.rating,
|
|
50
53
|
config: {
|
|
51
|
-
...normalizedConfig.rating.config,
|
|
52
54
|
title: normalizedConfig.rating.config?.title || t("settings.rating.title"),
|
|
53
55
|
description: normalizedConfig.rating.config?.description || t("settings.rating.description"),
|
|
56
|
+
storeUrl: normalizedConfig.rating.config?.storeUrl,
|
|
57
|
+
onRate: normalizedConfig.rating.config?.onRate,
|
|
54
58
|
}
|
|
55
59
|
}}
|
|
56
60
|
feedbackModalTexts={{
|
|
@@ -11,9 +11,9 @@ import type { NormalizedConfig } from "../utils/normalizeConfig";
|
|
|
11
11
|
* Check if navigation screen exists or is likely available
|
|
12
12
|
*/
|
|
13
13
|
function isFeatureAvailable(
|
|
14
|
-
navigation:
|
|
14
|
+
navigation: { navigate: (route: string) => void } | null,
|
|
15
15
|
route?: string,
|
|
16
|
-
onPress?:
|
|
16
|
+
onPress?: (() => void) | undefined
|
|
17
17
|
): boolean {
|
|
18
18
|
// If we have an onPress, it's definitely available
|
|
19
19
|
if (onPress) return true;
|
|
@@ -31,7 +31,7 @@ function isFeatureAvailable(
|
|
|
31
31
|
*/
|
|
32
32
|
export function useFeatureDetection(
|
|
33
33
|
normalizedConfig: NormalizedConfig,
|
|
34
|
-
navigation:
|
|
34
|
+
navigation: { navigate: (route: string) => void } | null,
|
|
35
35
|
options?: {
|
|
36
36
|
notificationServiceAvailable?: boolean;
|
|
37
37
|
},
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { FeatureVisibility } from "./BaseTypes";
|
|
7
7
|
import type { FeedbackType } from "../../../domains/feedback/domain/entities/FeedbackEntity";
|
|
8
|
+
import type { FAQCategory } from "../../../domains/faqs/domain/entities/FAQEntity";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* User Profile Settings Configuration
|
|
@@ -39,7 +40,7 @@ export interface FeedbackConfig {
|
|
|
39
40
|
/** Initial feedback type */
|
|
40
41
|
initialType?: FeedbackType;
|
|
41
42
|
/** Feedback submission handler */
|
|
42
|
-
onSubmit?: (data: { type:
|
|
43
|
+
onSubmit?: (data: { type: FeedbackType; rating: number; description: string; title: string }) => Promise<void>;
|
|
43
44
|
/** Custom handler to open feedback screen (overrides default modal) */
|
|
44
45
|
onPress?: () => void;
|
|
45
46
|
}
|
|
@@ -59,7 +60,7 @@ export interface FAQConfig {
|
|
|
59
60
|
/** Custom section title for grouping */
|
|
60
61
|
sectionTitle?: string;
|
|
61
62
|
/** FAQ items passed from app */
|
|
62
|
-
items?:
|
|
63
|
+
items?: FAQCategory[];
|
|
63
64
|
/** Handler to open FAQ screen */
|
|
64
65
|
onPress?: () => void;
|
|
65
66
|
}
|