@umituz/react-native-settings 5.3.81 → 5.4.1
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 +7 -9
- package/src/domains/about/presentation/components/AboutSection.tsx +5 -10
- package/src/domains/appearance/presentation/components/AppearanceSection.tsx +5 -10
- package/src/domains/faqs/presentation/components/FAQCategory.tsx +29 -10
- package/src/domains/gamification/components/GamificationScreen/AchievementsList.tsx +4 -1
- package/src/domains/gamification/components/GamificationScreen/StatsGrid.tsx +31 -10
- package/src/domains/gamification/hooks/useGamification.ts +9 -5
- package/src/domains/gamification/store/gamificationStore.ts +64 -11
- package/src/domains/gamification/types/index.ts +2 -0
- package/src/domains/gamification/utils/calculations.ts +9 -4
- package/src/domains/legal/presentation/components/LegalSection.tsx +5 -10
- package/src/domains/localization/index.ts +1 -1
- package/src/domains/localization/infrastructure/components/useLanguageNavigation.ts +4 -5
- package/src/domains/localization/infrastructure/config/NamespaceResolver.ts +1 -1
- package/src/domains/localization/infrastructure/config/i18n.ts +1 -1
- package/src/domains/localization/infrastructure/hooks/useTranslation.ts +1 -1
- package/src/domains/localization/infrastructure/storage/LanguageInitializer.ts +1 -1
- package/src/domains/localization/infrastructure/storage/LanguageSwitcher.ts +1 -1
- package/src/domains/notifications/infrastructure/services/NotificationScheduler.ts +36 -29
- package/src/domains/notifications/presentation/components/NotificationsSection.tsx +6 -9
- package/src/domains/notifications/presentation/hooks/useTimePicker.ts +10 -5
- package/src/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.ts +8 -8
- package/src/presentation/navigation/SettingsStackNavigator.tsx +3 -3
- package/src/presentation/navigation/hooks/useSettingsNavigation.ts +21 -17
- package/src/presentation/navigation/utils/navigationHelpers.ts +2 -2
- package/src/presentation/screens/components/sections/CustomSettingsList.tsx +23 -12
- package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +5 -8
- package/src/account.ts +0 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-settings",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.1",
|
|
4
4
|
"description": "Complete settings hub for React Native apps - consolidated package with settings, localization, about, legal, appearance, feedback, FAQs, rating, and gamification - expo-store-review and expo-device now lazy loaded",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -76,11 +76,6 @@
|
|
|
76
76
|
"react-native": "./src/domains/video-tutorials/index.ts",
|
|
77
77
|
"default": "./src/domains/video-tutorials/index.ts"
|
|
78
78
|
},
|
|
79
|
-
"./account": {
|
|
80
|
-
"types": "./dist/account.d.ts",
|
|
81
|
-
"react-native": "./src/account.ts",
|
|
82
|
-
"default": "./src/account.ts"
|
|
83
|
-
},
|
|
84
79
|
"./package.json": "./package.json"
|
|
85
80
|
},
|
|
86
81
|
"scripts": {
|
|
@@ -118,8 +113,9 @@
|
|
|
118
113
|
"url": "https://github.com/umituz/react-native-settings"
|
|
119
114
|
},
|
|
120
115
|
"optionalDependencies": {
|
|
121
|
-
"expo-
|
|
122
|
-
"expo-device": "~8.0.0"
|
|
116
|
+
"expo-constants": "~55.0.0",
|
|
117
|
+
"expo-device": "~8.0.0",
|
|
118
|
+
"expo-store-review": "~6.0.0"
|
|
123
119
|
},
|
|
124
120
|
"peerDependencies": {
|
|
125
121
|
"@expo/vector-icons": ">=14.0.0",
|
|
@@ -128,6 +124,7 @@
|
|
|
128
124
|
"@tanstack/react-query": ">=5.0.0",
|
|
129
125
|
"@umituz/react-native-design-system": "*",
|
|
130
126
|
"expo": ">=54.0.0",
|
|
127
|
+
"expo-constants": ">=55.0.0",
|
|
131
128
|
"expo-device": ">=6.0.0",
|
|
132
129
|
"expo-haptics": ">=15.0.0",
|
|
133
130
|
"expo-localization": ">=16.0.0",
|
|
@@ -165,7 +162,7 @@
|
|
|
165
162
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
166
163
|
"@typescript-eslint/parser": "^7.18.0",
|
|
167
164
|
"@umituz/react-native-auth": "^4.3.39",
|
|
168
|
-
"@umituz/react-native-design-system": "
|
|
165
|
+
"@umituz/react-native-design-system": "^4.27.0",
|
|
169
166
|
"@umituz/react-native-firebase": "^2.4.55",
|
|
170
167
|
"@umituz/react-native-sentry": "latest",
|
|
171
168
|
"eslint": "^8.57.0",
|
|
@@ -174,6 +171,7 @@
|
|
|
174
171
|
"eslint-plugin-react-native": "^5.0.0",
|
|
175
172
|
"expo-apple-authentication": "^8.0.8",
|
|
176
173
|
"expo-clipboard": "^8.0.8",
|
|
174
|
+
"expo-constants": "~55.0.0",
|
|
177
175
|
"expo-crypto": "^15.0.8",
|
|
178
176
|
"expo-device": "~7.0.0",
|
|
179
177
|
"expo-file-system": "^19.0.21",
|
|
@@ -3,6 +3,7 @@ import { ViewStyle } from 'react-native';
|
|
|
3
3
|
import { AboutConfig } from '../../domain/entities/AppInfo';
|
|
4
4
|
import { SettingsItemCard } from '../../../../presentation/components/SettingsItemCard';
|
|
5
5
|
import { useSettingsNavigation } from '../../../../presentation/navigation/hooks/useSettingsNavigation';
|
|
6
|
+
import { createRouteOrPressHandler } from '../../../../presentation/navigation/utils/navigationHelpers';
|
|
6
7
|
|
|
7
8
|
export interface AboutSectionProps {
|
|
8
9
|
config?: AboutConfig;
|
|
@@ -29,20 +30,14 @@ export const AboutSection: React.FC<AboutSectionProps> = ({
|
|
|
29
30
|
|
|
30
31
|
const navigation = useSettingsNavigation();
|
|
31
32
|
|
|
32
|
-
const route = config?.route || config?.defaultRoute || 'About';
|
|
33
33
|
const title = propsTitle || config?.title;
|
|
34
34
|
const description = propsDescription || config?.description;
|
|
35
35
|
const sectionTitle = propsSectionTitle;
|
|
36
36
|
|
|
37
|
-
const handlePress = (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
config.onPress();
|
|
42
|
-
} else {
|
|
43
|
-
navigation.navigate(route as 'About');
|
|
44
|
-
}
|
|
45
|
-
};
|
|
37
|
+
const handlePress = createRouteOrPressHandler(navigation.navigate, {
|
|
38
|
+
route: config?.route || config?.defaultRoute || 'About',
|
|
39
|
+
onPress: onPress || config?.onPress,
|
|
40
|
+
});
|
|
46
41
|
|
|
47
42
|
if (!title) return null;
|
|
48
43
|
|
|
@@ -3,6 +3,7 @@ import { ViewStyle } from 'react-native';
|
|
|
3
3
|
import { AppearanceSectionConfig } from '../../types';
|
|
4
4
|
import { SettingsItemCard } from '../../../../presentation/components/SettingsItemCard';
|
|
5
5
|
import { useSettingsNavigation } from '../../../../presentation/navigation/hooks/useSettingsNavigation';
|
|
6
|
+
import { createRouteOrPressHandler } from '../../../../presentation/navigation/utils/navigationHelpers';
|
|
6
7
|
|
|
7
8
|
export interface AppearanceSectionProps {
|
|
8
9
|
config?: AppearanceSectionConfig;
|
|
@@ -31,19 +32,13 @@ export const AppearanceSection: React.FC<AppearanceSectionProps> = ({
|
|
|
31
32
|
|
|
32
33
|
const navigation = useSettingsNavigation();
|
|
33
34
|
|
|
34
|
-
const route = config?.route || config?.defaultRoute || 'Appearance';
|
|
35
35
|
const title = titleProp || config?.title;
|
|
36
36
|
const description = descriptionProp || config?.description;
|
|
37
37
|
|
|
38
|
-
const handlePress = (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
config.onPress();
|
|
43
|
-
} else {
|
|
44
|
-
navigation.navigate(route as 'Appearance');
|
|
45
|
-
}
|
|
46
|
-
};
|
|
38
|
+
const handlePress = createRouteOrPressHandler(navigation.navigate, {
|
|
39
|
+
route: config?.route || config?.defaultRoute || 'Appearance',
|
|
40
|
+
onPress: onPress || config?.onPress,
|
|
41
|
+
});
|
|
47
42
|
|
|
48
43
|
if (!title) return null;
|
|
49
44
|
|
|
@@ -24,6 +24,29 @@ export interface FAQCategoryProps {
|
|
|
24
24
|
styles?: FAQCategoryStyles;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// Memoized FAQ items list to prevent unnecessary re-renders when other categories update
|
|
28
|
+
const FAQItemsList: React.FC<{
|
|
29
|
+
items: FAQCategoryType['items'];
|
|
30
|
+
isExpanded: (itemId: string) => boolean;
|
|
31
|
+
onToggleItem: (itemId: string) => void;
|
|
32
|
+
customStyles?: FAQCategoryStyles;
|
|
33
|
+
}> = React.memo(({ items, isExpanded, onToggleItem, customStyles }) => (
|
|
34
|
+
<>
|
|
35
|
+
{items.map((item, index) => (
|
|
36
|
+
<FAQItemComponent
|
|
37
|
+
key={item.id}
|
|
38
|
+
item={item}
|
|
39
|
+
isExpanded={isExpanded(item.id)}
|
|
40
|
+
onToggle={() => onToggleItem(item.id)}
|
|
41
|
+
isLast={index === items.length - 1}
|
|
42
|
+
styles={customStyles?.itemStyles}
|
|
43
|
+
/>
|
|
44
|
+
))}
|
|
45
|
+
</>
|
|
46
|
+
));
|
|
47
|
+
|
|
48
|
+
FAQItemsList.displayName = "FAQItemsList";
|
|
49
|
+
|
|
27
50
|
export const FAQCategoryComponent: React.FC<FAQCategoryProps> = ({
|
|
28
51
|
category,
|
|
29
52
|
isExpanded,
|
|
@@ -66,16 +89,12 @@ export const FAQCategoryComponent: React.FC<FAQCategoryProps> = ({
|
|
|
66
89
|
</AtomicText>
|
|
67
90
|
<View style={styles.titleLine} />
|
|
68
91
|
</View>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
isLast={index === category.items.length - 1}
|
|
76
|
-
styles={customStyles?.itemStyles}
|
|
77
|
-
/>
|
|
78
|
-
))}
|
|
92
|
+
<FAQItemsList
|
|
93
|
+
items={category.items}
|
|
94
|
+
isExpanded={isExpanded}
|
|
95
|
+
onToggleItem={onToggleItem}
|
|
96
|
+
customStyles={customStyles}
|
|
97
|
+
/>
|
|
79
98
|
</View>
|
|
80
99
|
);
|
|
81
100
|
};
|
|
@@ -54,13 +54,16 @@ export const AchievementsList: React.FC<AchievementsListProps> = ({
|
|
|
54
54
|
);
|
|
55
55
|
}, [achievements]);
|
|
56
56
|
|
|
57
|
+
// Memoize empty state check to avoid recalculation
|
|
58
|
+
const showEmptyState = achievements.length === 0 && emptyAchievementsText;
|
|
59
|
+
|
|
57
60
|
return (
|
|
58
61
|
<View style={styles.section}>
|
|
59
62
|
<AtomicText style={[styles.sectionTitle, { color: textColor }, sectionTitleStyle]}>
|
|
60
63
|
{achievementsTitle}
|
|
61
64
|
</AtomicText>
|
|
62
65
|
|
|
63
|
-
{
|
|
66
|
+
{showEmptyState ? (
|
|
64
67
|
<AtomicText style={[styles.emptyText, { color: subtextColor }]}>
|
|
65
68
|
{emptyAchievementsText}
|
|
66
69
|
</AtomicText>
|
|
@@ -9,6 +9,30 @@ import { StatsCard } from "../StatsCard";
|
|
|
9
9
|
import { styles } from "./styles";
|
|
10
10
|
import type { StatsCardProps } from "../StatsCard";
|
|
11
11
|
|
|
12
|
+
// Memoized stats list to prevent unnecessary re-renders when parent updates
|
|
13
|
+
const StatsList: React.FC<{
|
|
14
|
+
stats: StatsGridProps['stats'];
|
|
15
|
+
accentColor: string;
|
|
16
|
+
cardBackgroundColor: string;
|
|
17
|
+
textColor: string;
|
|
18
|
+
subtextColor: string;
|
|
19
|
+
}> = React.memo(({ stats, accentColor, cardBackgroundColor, textColor, subtextColor }) => (
|
|
20
|
+
<>
|
|
21
|
+
{stats.map((stat) => (
|
|
22
|
+
<StatsCard
|
|
23
|
+
key={`${stat.label}-${stat.value}`}
|
|
24
|
+
{...stat}
|
|
25
|
+
accentColor={accentColor}
|
|
26
|
+
backgroundColor={cardBackgroundColor}
|
|
27
|
+
textColor={textColor}
|
|
28
|
+
subtextColor={subtextColor}
|
|
29
|
+
/>
|
|
30
|
+
))}
|
|
31
|
+
</>
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
StatsList.displayName = "StatsList";
|
|
35
|
+
|
|
12
36
|
export interface StatsGridProps {
|
|
13
37
|
statsTitle: string;
|
|
14
38
|
stats: Array<Omit<StatsCardProps, "accentColor" | "backgroundColor" | "textColor" | "subtextColor">>;
|
|
@@ -36,16 +60,13 @@ export const StatsGrid: React.FC<StatsGridProps> = ({
|
|
|
36
60
|
{statsTitle}
|
|
37
61
|
</AtomicText>
|
|
38
62
|
<View style={styles.statsGrid}>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
subtextColor={subtextColor}
|
|
47
|
-
/>
|
|
48
|
-
))}
|
|
63
|
+
<StatsList
|
|
64
|
+
stats={stats}
|
|
65
|
+
accentColor={accentColor}
|
|
66
|
+
cardBackgroundColor={cardBackgroundColor}
|
|
67
|
+
textColor={textColor}
|
|
68
|
+
subtextColor={subtextColor}
|
|
69
|
+
/>
|
|
49
70
|
</View>
|
|
50
71
|
</View>
|
|
51
72
|
);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { useCallback, useEffect, useMemo } from "react";
|
|
7
7
|
import { useGamificationStore } from "../store/gamificationStore";
|
|
8
|
-
import { calculateLevel } from "../utils/calculations";
|
|
8
|
+
import { calculateLevel, DEFAULT_POINTS_PER_LEVEL, MAX_PROGRESS } from "../utils/calculations";
|
|
9
9
|
import type { GamificationConfig, LevelState, Achievement } from "../types";
|
|
10
10
|
|
|
11
11
|
export interface UseGamificationReturn {
|
|
@@ -33,9 +33,13 @@ export const useGamification = (
|
|
|
33
33
|
// Auto-initialize if config provided
|
|
34
34
|
useEffect(() => {
|
|
35
35
|
if (config && !store.isInitialized) {
|
|
36
|
-
store.initialize(config)
|
|
36
|
+
store.initialize(config).catch((error) => {
|
|
37
|
+
// Log error but don't crash - initialization is not critical
|
|
38
|
+
console.error('[useGamification] Failed to initialize:', error);
|
|
39
|
+
});
|
|
37
40
|
}
|
|
38
|
-
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
}, [config, store.isInitialized]);
|
|
39
43
|
|
|
40
44
|
// Calculate level from config
|
|
41
45
|
const level = useMemo((): LevelState => {
|
|
@@ -43,8 +47,8 @@ export const useGamification = (
|
|
|
43
47
|
return {
|
|
44
48
|
currentLevel: 1,
|
|
45
49
|
currentPoints: store.points,
|
|
46
|
-
pointsToNext:
|
|
47
|
-
progress: Math.min(
|
|
50
|
+
pointsToNext: DEFAULT_POINTS_PER_LEVEL,
|
|
51
|
+
progress: Math.min(MAX_PROGRESS, (store.points / DEFAULT_POINTS_PER_LEVEL) * MAX_PROGRESS),
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
54
|
return calculateLevel(store.points, config.levels);
|
|
@@ -30,8 +30,6 @@ const DEFAULT_STATE: GamificationState = {
|
|
|
30
30
|
isInitialized: false,
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
let currentConfig: GamificationConfig | null = null;
|
|
34
|
-
|
|
35
33
|
export const useGamificationStore = createStore<GamificationState, GamificationActions>({
|
|
36
34
|
name: "gamification-storage",
|
|
37
35
|
initialState: DEFAULT_STATE,
|
|
@@ -45,12 +43,15 @@ export const useGamificationStore = createStore<GamificationState, GamificationA
|
|
|
45
43
|
streak: state.streak,
|
|
46
44
|
isLoading: false,
|
|
47
45
|
isInitialized: false,
|
|
46
|
+
// Don't persist _config - it's runtime only
|
|
48
47
|
}),
|
|
49
48
|
actions: (set, get) => ({
|
|
50
49
|
initialize: async (config: GamificationConfig) => {
|
|
51
|
-
currentConfig = config;
|
|
52
50
|
const state = get();
|
|
53
51
|
|
|
52
|
+
// Store config in state (not module-level)
|
|
53
|
+
set({ _config: config, isInitialized: true });
|
|
54
|
+
|
|
54
55
|
// Initialize achievements from config
|
|
55
56
|
const achievements: Achievement[] = config.achievements.map((def) => ({
|
|
56
57
|
...def,
|
|
@@ -67,7 +68,7 @@ export const useGamificationStore = createStore<GamificationState, GamificationA
|
|
|
67
68
|
return ach;
|
|
68
69
|
});
|
|
69
70
|
|
|
70
|
-
set({ achievements: mergedAchievements
|
|
71
|
+
set({ achievements: mergedAchievements });
|
|
71
72
|
},
|
|
72
73
|
|
|
73
74
|
addPoints: (amount: number) => {
|
|
@@ -77,7 +78,7 @@ export const useGamificationStore = createStore<GamificationState, GamificationA
|
|
|
77
78
|
|
|
78
79
|
completeTask: () => {
|
|
79
80
|
const state = get();
|
|
80
|
-
const pointsToAdd =
|
|
81
|
+
const pointsToAdd = state._config?.pointsPerAction ?? 15;
|
|
81
82
|
|
|
82
83
|
const newTotalTasks = state.totalTasksCompleted + 1;
|
|
83
84
|
|
|
@@ -86,9 +87,63 @@ export const useGamificationStore = createStore<GamificationState, GamificationA
|
|
|
86
87
|
points: state.points + pointsToAdd,
|
|
87
88
|
});
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
// Update streak
|
|
91
|
+
const currentStreak = state.streak.lastActivityDate
|
|
92
|
+
? new Date(state.streak.lastActivityDate)
|
|
93
|
+
: null;
|
|
94
|
+
const now = new Date();
|
|
95
|
+
|
|
96
|
+
let newStreak = state.streak.current;
|
|
97
|
+
|
|
98
|
+
if (!currentStreak || !isSameDay(currentStreak, now)) {
|
|
99
|
+
if (isStreakActive(state.streak.lastActivityDate)) {
|
|
100
|
+
newStreak = state.streak.current + 1;
|
|
101
|
+
} else {
|
|
102
|
+
newStreak = 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
set({
|
|
107
|
+
streak: {
|
|
108
|
+
current: newStreak,
|
|
109
|
+
longest: Math.max(state.streak.longest, newStreak),
|
|
110
|
+
lastActivityDate: now.toISOString(),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Check achievements
|
|
115
|
+
if (!state.achievements || state.achievements.length === 0) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const updatedAchievements = state.achievements.map((ach: Achievement) => {
|
|
120
|
+
if (ach.isUnlocked) return ach;
|
|
121
|
+
|
|
122
|
+
const progress = updateAchievementProgress(
|
|
123
|
+
ach,
|
|
124
|
+
newTotalTasks,
|
|
125
|
+
newStreak
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const shouldUnlock = checkAchievementUnlock(
|
|
129
|
+
ach,
|
|
130
|
+
newTotalTasks,
|
|
131
|
+
newStreak
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
if (shouldUnlock) {
|
|
135
|
+
return {
|
|
136
|
+
...ach,
|
|
137
|
+
isUnlocked: true,
|
|
138
|
+
unlockedAt: new Date().toISOString(),
|
|
139
|
+
progress: 100,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return { ...ach, progress };
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
set({ achievements: updatedAchievements });
|
|
92
147
|
},
|
|
93
148
|
|
|
94
149
|
updateStreak: () => {
|
|
@@ -118,8 +173,6 @@ export const useGamificationStore = createStore<GamificationState, GamificationA
|
|
|
118
173
|
},
|
|
119
174
|
|
|
120
175
|
checkAchievements: (): Achievement[] => {
|
|
121
|
-
if (!currentConfig) return [];
|
|
122
|
-
|
|
123
176
|
const state = get();
|
|
124
177
|
|
|
125
178
|
if (!state.achievements || state.achievements.length === 0) {
|
|
@@ -143,7 +196,7 @@ export const useGamificationStore = createStore<GamificationState, GamificationA
|
|
|
143
196
|
state.streak.current
|
|
144
197
|
);
|
|
145
198
|
|
|
146
|
-
if (shouldUnlock
|
|
199
|
+
if (shouldUnlock) {
|
|
147
200
|
const unlocked = {
|
|
148
201
|
...ach,
|
|
149
202
|
isUnlocked: true,
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import type { LevelDefinition, LevelState, AchievementDefinition } from "../types";
|
|
7
7
|
|
|
8
|
+
// Constants for gamification calculations
|
|
9
|
+
export const DEFAULT_LEVEL_RANGE = 100;
|
|
10
|
+
export const DEFAULT_POINTS_PER_LEVEL = 50;
|
|
11
|
+
export const MAX_PROGRESS = 100;
|
|
12
|
+
|
|
8
13
|
export const calculateLevel = (
|
|
9
14
|
points: number,
|
|
10
15
|
levels: LevelDefinition[]
|
|
@@ -16,7 +21,7 @@ export const calculateLevel = (
|
|
|
16
21
|
return {
|
|
17
22
|
currentLevel: 1,
|
|
18
23
|
currentPoints: points,
|
|
19
|
-
pointsToNext:
|
|
24
|
+
pointsToNext: DEFAULT_POINTS_PER_LEVEL,
|
|
20
25
|
progress: 0,
|
|
21
26
|
};
|
|
22
27
|
}
|
|
@@ -34,8 +39,8 @@ export const calculateLevel = (
|
|
|
34
39
|
const pointsInLevel = points - currentLevelDef.minPoints;
|
|
35
40
|
const levelRange = nextLevelDef
|
|
36
41
|
? nextLevelDef.minPoints - currentLevelDef.minPoints
|
|
37
|
-
:
|
|
38
|
-
const progress = Math.min(
|
|
42
|
+
: DEFAULT_LEVEL_RANGE;
|
|
43
|
+
const progress = Math.min(MAX_PROGRESS, (pointsInLevel / levelRange) * MAX_PROGRESS);
|
|
39
44
|
const pointsToNext = nextLevelDef
|
|
40
45
|
? nextLevelDef.minPoints - points
|
|
41
46
|
: 0;
|
|
@@ -70,7 +75,7 @@ export const updateAchievementProgress = (
|
|
|
70
75
|
currentStreak: number
|
|
71
76
|
): number => {
|
|
72
77
|
const value = definition.type === "streak" ? currentStreak : tasksCompleted;
|
|
73
|
-
return Math.min(
|
|
78
|
+
return Math.min(MAX_PROGRESS, (value / definition.threshold) * MAX_PROGRESS);
|
|
74
79
|
};
|
|
75
80
|
|
|
76
81
|
/**
|
|
@@ -3,6 +3,7 @@ import { ViewStyle } from 'react-native';
|
|
|
3
3
|
import { LegalConfig } from '../../domain/entities/LegalConfig';
|
|
4
4
|
import { SettingsItemCard } from '../../../../presentation/components/SettingsItemCard';
|
|
5
5
|
import { useSettingsNavigation } from '../../../../presentation/navigation/hooks/useSettingsNavigation';
|
|
6
|
+
import { createRouteOrPressHandler } from '../../../../presentation/navigation/utils/navigationHelpers';
|
|
6
7
|
|
|
7
8
|
export interface LegalSectionProps {
|
|
8
9
|
config?: LegalConfig;
|
|
@@ -29,20 +30,14 @@ export const LegalSection: React.FC<LegalSectionProps> = ({
|
|
|
29
30
|
|
|
30
31
|
const navigation = useSettingsNavigation();
|
|
31
32
|
|
|
32
|
-
const route = config?.route || config?.defaultRoute || 'Legal';
|
|
33
33
|
const title = propsTitle || config?.title;
|
|
34
34
|
const description = propsDescription || config?.description;
|
|
35
35
|
const sectionTitle = propsSectionTitle;
|
|
36
36
|
|
|
37
|
-
const handlePress = (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
config.onPress();
|
|
42
|
-
} else {
|
|
43
|
-
navigation.navigate(route as 'Legal');
|
|
44
|
-
}
|
|
45
|
-
};
|
|
37
|
+
const handlePress = createRouteOrPressHandler(navigation.navigate, {
|
|
38
|
+
route: config?.route || config?.defaultRoute || 'Legal',
|
|
39
|
+
onPress: onPress || config?.onPress,
|
|
40
|
+
});
|
|
46
41
|
|
|
47
42
|
if (!title) return null;
|
|
48
43
|
|
|
@@ -19,7 +19,7 @@ export { LanguageSection } from './presentation/components/LanguageSection';
|
|
|
19
19
|
export type { LanguageSectionProps, LanguageSectionConfig } from './presentation/components/LanguageSection';
|
|
20
20
|
|
|
21
21
|
// Configuration
|
|
22
|
-
export {
|
|
22
|
+
export { i18n } from './infrastructure/config/i18n';
|
|
23
23
|
export { I18nInitializer } from './infrastructure/config/I18nInitializer';
|
|
24
24
|
export {
|
|
25
25
|
DEFAULT_LANGUAGE,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useLocalization } from '../hooks/useLocalization';
|
|
2
2
|
import { languageRepository } from '../repository/LanguageRepository';
|
|
3
3
|
import { useSettingsNavigation } from '../../../../presentation/navigation/hooks/useSettingsNavigation';
|
|
4
|
+
import { createRouteOrPressHandler } from '../../../../presentation/navigation/utils/navigationHelpers';
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
export const useLanguageNavigation = (navigationScreen: string) => {
|
|
@@ -8,11 +9,9 @@ export const useLanguageNavigation = (navigationScreen: string) => {
|
|
|
8
9
|
const { currentLanguage } = useLocalization();
|
|
9
10
|
const currentLang = languageRepository.getLanguageByCode(currentLanguage) || languageRepository.getDefaultLanguage();
|
|
10
11
|
|
|
11
|
-
const navigateToLanguageSelection = (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
};
|
|
12
|
+
const navigateToLanguageSelection = createRouteOrPressHandler(navigation.navigate, {
|
|
13
|
+
route: navigationScreen,
|
|
14
|
+
});
|
|
16
15
|
|
|
17
16
|
return { currentLang, navigateToLanguageSelection };
|
|
18
17
|
};
|
|
@@ -32,7 +32,7 @@ export class NamespaceResolver {
|
|
|
32
32
|
appNamespaces = Object.keys(langTranslations);
|
|
33
33
|
}
|
|
34
34
|
} else {
|
|
35
|
-
// If structured by namespace
|
|
35
|
+
// If structured by namespace, keys are namespaces
|
|
36
36
|
appNamespaces = Object.keys(appTranslations);
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { useCallback } from 'react';
|
|
10
10
|
import { useTranslation } from 'react-i18next';
|
|
11
|
-
import i18n from '../config/i18n';
|
|
11
|
+
import { i18n } from '../config/i18n';
|
|
12
12
|
import { devWarn } from '../../../../utils/devUtils';
|
|
13
13
|
|
|
14
14
|
export interface TranslationOptions {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { storageRepository } from '@umituz/react-native-design-system/storage';
|
|
11
|
-
import i18n from '../config/i18n';
|
|
11
|
+
import { i18n } from '../config/i18n';
|
|
12
12
|
import { languageRepository } from '../repository/LanguageRepository';
|
|
13
13
|
import { getDeviceLocale } from '../config/languages';
|
|
14
14
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { storageRepository } from '@umituz/react-native-design-system/storage';
|
|
10
|
-
import i18n from '../config/i18n';
|
|
10
|
+
import { i18n } from '../config/i18n';
|
|
11
11
|
import { languageRepository } from '../repository/LanguageRepository';
|
|
12
12
|
import { isDev } from '../../../../utils/devUtils';
|
|
13
13
|
|
|
@@ -7,36 +7,43 @@ export class NotificationScheduler {
|
|
|
7
7
|
try {
|
|
8
8
|
const { title, body, data = {}, trigger, sound = true, badge, categoryIdentifier } = options;
|
|
9
9
|
|
|
10
|
-
let notificationTrigger: Notifications.NotificationTriggerInput
|
|
10
|
+
let notificationTrigger: Notifications.NotificationTriggerInput;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
12
|
+
switch (trigger.type) {
|
|
13
|
+
case 'date':
|
|
14
|
+
notificationTrigger = {
|
|
15
|
+
date: trigger.date,
|
|
16
|
+
channelId: categoryIdentifier || 'default',
|
|
17
|
+
};
|
|
18
|
+
break;
|
|
19
|
+
case 'daily':
|
|
20
|
+
notificationTrigger = {
|
|
21
|
+
hour: trigger.hour,
|
|
22
|
+
minute: trigger.minute,
|
|
23
|
+
repeats: true,
|
|
24
|
+
channelId: categoryIdentifier || 'reminders',
|
|
25
|
+
};
|
|
26
|
+
break;
|
|
27
|
+
case 'weekly':
|
|
28
|
+
notificationTrigger = {
|
|
29
|
+
weekday: trigger.weekday,
|
|
30
|
+
hour: trigger.hour,
|
|
31
|
+
minute: trigger.minute,
|
|
32
|
+
repeats: true,
|
|
33
|
+
channelId: categoryIdentifier || 'reminders',
|
|
34
|
+
};
|
|
35
|
+
break;
|
|
36
|
+
case 'monthly':
|
|
37
|
+
notificationTrigger = {
|
|
38
|
+
day: trigger.day,
|
|
39
|
+
hour: trigger.hour,
|
|
40
|
+
minute: trigger.minute,
|
|
41
|
+
repeats: true,
|
|
42
|
+
channelId: categoryIdentifier || 'reminders',
|
|
43
|
+
};
|
|
44
|
+
break;
|
|
45
|
+
default:
|
|
46
|
+
throw new Error(`Unsupported trigger type: ${(trigger as { type: string }).type}`);
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
const notificationId = await Notifications.scheduleNotificationAsync({
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import type { StyleProp, ViewStyle } from 'react-native';
|
|
3
3
|
import { SettingsItemCard } from '../../../../presentation/components/SettingsItemCard';
|
|
4
4
|
import { useSettingsNavigation } from '../../../../presentation/navigation/hooks/useSettingsNavigation';
|
|
5
|
+
import { createRouteOrPressHandler } from '../../../../presentation/navigation/utils/navigationHelpers';
|
|
5
6
|
|
|
6
7
|
export interface NotificationsSectionConfig {
|
|
7
8
|
route?: string;
|
|
@@ -26,14 +27,10 @@ export const NotificationsSection: React.FC<NotificationsSectionProps> = ({
|
|
|
26
27
|
}) => {
|
|
27
28
|
const navigation = useSettingsNavigation();
|
|
28
29
|
|
|
29
|
-
const handlePress =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const route = config?.route || 'Notifications';
|
|
34
|
-
navigation.navigate(route as 'Notifications');
|
|
35
|
-
}
|
|
36
|
-
}, [config?.route, config?.onPress, navigation]);
|
|
30
|
+
const handlePress = createRouteOrPressHandler(navigation.navigate, {
|
|
31
|
+
route: config?.route || ('Notifications' as const),
|
|
32
|
+
onPress: config?.onPress,
|
|
33
|
+
});
|
|
37
34
|
|
|
38
35
|
const title = config?.title || "";
|
|
39
36
|
const description = config?.description || "";
|
|
@@ -62,13 +62,18 @@ export const useTimePicker = ({
|
|
|
62
62
|
}, [onStartTimeChange, onEndTimeChange]);
|
|
63
63
|
|
|
64
64
|
const getPickerDate = useCallback((): Date => {
|
|
65
|
-
const date = new Date();
|
|
66
65
|
if (pickerMode === 'start') {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
// Don't mutate - create new Date with hours/minutes
|
|
67
|
+
return new Date(
|
|
68
|
+
new Date().setHours(quietHours.startHour, quietHours.startMinute, 0, 0)
|
|
69
|
+
);
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
if (pickerMode === 'end') {
|
|
72
|
+
return new Date(
|
|
73
|
+
new Date().setHours(quietHours.endHour, quietHours.endMinute, 0, 0)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return new Date();
|
|
72
77
|
}, [pickerMode, quietHours]);
|
|
73
78
|
|
|
74
79
|
return {
|
|
@@ -11,26 +11,26 @@ export const useQuietHoursActions = () => {
|
|
|
11
11
|
const quietHours = useQuietHours();
|
|
12
12
|
const { updateQuietHours } = useNotificationStore();
|
|
13
13
|
|
|
14
|
-
const setQuietHoursEnabled = useCallback(
|
|
14
|
+
const setQuietHoursEnabled = useCallback((enabled: boolean): void => {
|
|
15
15
|
// Use getState() to avoid stale closure and race conditions
|
|
16
16
|
const currentQuietHours = useNotificationStore.getState().preferences.quietHours;
|
|
17
|
-
|
|
17
|
+
updateQuietHours({ ...currentQuietHours, enabled });
|
|
18
18
|
}, [updateQuietHours]);
|
|
19
19
|
|
|
20
|
-
const setStartTime = useCallback(
|
|
20
|
+
const setStartTime = useCallback((hour: number, minute: number): void => {
|
|
21
21
|
// Use getState() to avoid stale closure and race conditions
|
|
22
22
|
const currentQuietHours = useNotificationStore.getState().preferences.quietHours;
|
|
23
|
-
|
|
23
|
+
updateQuietHours({ ...currentQuietHours, startHour: hour, startMinute: minute });
|
|
24
24
|
}, [updateQuietHours]);
|
|
25
25
|
|
|
26
|
-
const setEndTime = useCallback(
|
|
26
|
+
const setEndTime = useCallback((hour: number, minute: number): void => {
|
|
27
27
|
// Use getState() to avoid stale closure and race conditions
|
|
28
28
|
const currentQuietHours = useNotificationStore.getState().preferences.quietHours;
|
|
29
|
-
|
|
29
|
+
updateQuietHours({ ...currentQuietHours, endHour: hour, endMinute: minute });
|
|
30
30
|
}, [updateQuietHours]);
|
|
31
31
|
|
|
32
|
-
const setQuietHours = useCallback(
|
|
33
|
-
|
|
32
|
+
const setQuietHours = useCallback((config: QuietHoursConfig): void => {
|
|
33
|
+
updateQuietHours(config);
|
|
34
34
|
}, [updateQuietHours]);
|
|
35
35
|
|
|
36
36
|
const isInQuietHours = useCallback((): boolean => {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React from "react";
|
|
9
|
-
import {
|
|
9
|
+
import { useAppNavigation } from "@umituz/react-native-design-system/molecules";
|
|
10
10
|
import { StackNavigator, type StackNavigatorConfig } from "@umituz/react-native-design-system/molecules";
|
|
11
11
|
import { useNavigationHandlers, useSettingsScreens } from "./hooks";
|
|
12
12
|
import {
|
|
@@ -43,7 +43,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = (pr
|
|
|
43
43
|
);
|
|
44
44
|
|
|
45
45
|
// Get navigation for custom sections
|
|
46
|
-
const navigation =
|
|
46
|
+
const navigation = useAppNavigation();
|
|
47
47
|
|
|
48
48
|
const screens = useSettingsScreens({
|
|
49
49
|
...props,
|
|
@@ -51,7 +51,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = (pr
|
|
|
51
51
|
legalProps: legalScreenProps,
|
|
52
52
|
notificationTranslations,
|
|
53
53
|
quietHoursTranslations,
|
|
54
|
-
navigation,
|
|
54
|
+
navigation: navigation as unknown as Record<string, unknown>,
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
const navigatorConfig: StackNavigatorConfig<SettingsStackParamList> = {
|
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Settings Navigation Hook
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
5
|
-
*
|
|
4
|
+
* Provides standardized navigation for Settings stack screens.
|
|
5
|
+
* Uses useAppNavigation from design system for consistency.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { useSettingsNavigation } from '@umituz/react-native-settings/presentation/navigation';
|
|
10
|
+
*
|
|
11
|
+
* function LanguageSelectionScreen() {
|
|
12
|
+
* const navigation = useSettingsNavigation();
|
|
13
|
+
* navigation.navigate('Appearance');
|
|
14
|
+
* navigation.goBack();
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
6
17
|
*/
|
|
7
18
|
|
|
8
|
-
import {
|
|
9
|
-
import type { StackNavigationProp } from '@react-navigation/stack';
|
|
10
|
-
import type { SettingsStackParamList } from '../types';
|
|
19
|
+
import { useAppNavigation } from '@umituz/react-native-design-system/molecules';
|
|
11
20
|
|
|
12
21
|
/**
|
|
13
|
-
*
|
|
22
|
+
* Navigation result type inferred from useAppNavigation
|
|
14
23
|
*/
|
|
15
|
-
|
|
24
|
+
type SettingsNavigation = ReturnType<typeof useAppNavigation>;
|
|
16
25
|
|
|
17
26
|
/**
|
|
18
|
-
* Hook to get
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```typescript
|
|
22
|
-
* const navigation = useSettingsNavigation();
|
|
23
|
-
* navigation.navigate('LanguageSelection'); // Fully typed!
|
|
24
|
-
* ```
|
|
27
|
+
* Hook to get navigation for Settings screens
|
|
28
|
+
* Delegates to useAppNavigation for consistent API
|
|
25
29
|
*/
|
|
26
|
-
export const useSettingsNavigation = () => {
|
|
27
|
-
return
|
|
30
|
+
export const useSettingsNavigation = (): SettingsNavigation => {
|
|
31
|
+
return useAppNavigation();
|
|
28
32
|
};
|
|
@@ -11,7 +11,7 @@ type NavigateFunction = <RouteName extends keyof SettingsStackParamList>(
|
|
|
11
11
|
) => void;
|
|
12
12
|
|
|
13
13
|
interface RouteOrPressConfig<T extends keyof SettingsStackParamList = keyof SettingsStackParamList> {
|
|
14
|
-
route?: T;
|
|
14
|
+
route?: T | string;
|
|
15
15
|
onPress?: () => void;
|
|
16
16
|
fallback?: T;
|
|
17
17
|
}
|
|
@@ -26,7 +26,7 @@ export const createRouteOrPressHandler = <T extends keyof SettingsStackParamList
|
|
|
26
26
|
} else {
|
|
27
27
|
const targetRoute = config.route || config.fallback;
|
|
28
28
|
if (targetRoute) {
|
|
29
|
-
navigate(targetRoute);
|
|
29
|
+
navigate(targetRoute as T);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
};
|
|
@@ -10,6 +10,26 @@ interface CustomSettingsListProps {
|
|
|
10
10
|
|
|
11
11
|
const EMPTY_SECTIONS: CustomSettingsSection[] = [];
|
|
12
12
|
|
|
13
|
+
// Extract to separate memoized component to prevent unnecessary re-renders when other sections change
|
|
14
|
+
const SettingsItemsList: React.FC<{ items: CustomSettingsSection['items'] }> = React.memo(({ items }) => (
|
|
15
|
+
<>
|
|
16
|
+
{items?.map((item, itemIndex) => (
|
|
17
|
+
<SettingsItemCard
|
|
18
|
+
key={item.id || `item-${itemIndex}`}
|
|
19
|
+
title={item.title}
|
|
20
|
+
description={item.subtitle}
|
|
21
|
+
icon={item.icon}
|
|
22
|
+
onPress={item.onPress}
|
|
23
|
+
rightIcon={item.rightIcon}
|
|
24
|
+
iconBgColor={item.iconBgColor}
|
|
25
|
+
iconColor={item.iconColor}
|
|
26
|
+
/>
|
|
27
|
+
))}
|
|
28
|
+
</>
|
|
29
|
+
));
|
|
30
|
+
|
|
31
|
+
SettingsItemsList.displayName = "SettingsItemsList";
|
|
32
|
+
|
|
13
33
|
export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({ customSections = EMPTY_SECTIONS }) => {
|
|
14
34
|
const sortedSections = useMemo(() => {
|
|
15
35
|
return Array.from(customSections)
|
|
@@ -26,18 +46,9 @@ export const CustomSettingsList: React.FC<CustomSettingsListProps> = ({ customSe
|
|
|
26
46
|
title={section.title}
|
|
27
47
|
>
|
|
28
48
|
{section.content}
|
|
29
|
-
{!section.content && section.items && section.items.length > 0 &&
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
title={item.title}
|
|
33
|
-
description={item.subtitle}
|
|
34
|
-
icon={item.icon}
|
|
35
|
-
onPress={item.onPress}
|
|
36
|
-
rightIcon={item.rightIcon}
|
|
37
|
-
iconBgColor={item.iconBgColor}
|
|
38
|
-
iconColor={item.iconColor}
|
|
39
|
-
/>
|
|
40
|
-
))}
|
|
49
|
+
{!section.content && section.items && section.items.length > 0 && (
|
|
50
|
+
<SettingsItemsList items={section.items} />
|
|
51
|
+
)}
|
|
41
52
|
</SettingsSection>
|
|
42
53
|
))}
|
|
43
54
|
</>
|
|
@@ -6,6 +6,7 @@ import { SettingsItemCard } from "../../../components/SettingsItemCard";
|
|
|
6
6
|
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|
|
7
7
|
import { SettingsSection } from "../../../components/SettingsSection";
|
|
8
8
|
import { useSettingsNavigation } from "../../../navigation/hooks/useSettingsNavigation";
|
|
9
|
+
import { createRouteOrPressHandler } from "../../../navigation/utils/navigationHelpers";
|
|
9
10
|
|
|
10
11
|
interface FeatureSettingsSectionProps {
|
|
11
12
|
normalizedConfig: NormalizedConfig;
|
|
@@ -25,14 +26,10 @@ export const FeatureSettingsSection: React.FC<FeatureSettingsSectionProps> = ({
|
|
|
25
26
|
const translations = normalizedConfig.translations;
|
|
26
27
|
const navigation = useSettingsNavigation();
|
|
27
28
|
|
|
28
|
-
const handleLanguagePress =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const route = normalizedConfig.language.config?.route || "LanguageSelection";
|
|
33
|
-
navigation.navigate(route as 'LanguageSelection');
|
|
34
|
-
}
|
|
35
|
-
}, [navigation, normalizedConfig.language.config]);
|
|
29
|
+
const handleLanguagePress = createRouteOrPressHandler(navigation.navigate, {
|
|
30
|
+
route: normalizedConfig.language.config?.route || "LanguageSelection",
|
|
31
|
+
onPress: normalizedConfig.language.config?.onPress,
|
|
32
|
+
});
|
|
36
33
|
|
|
37
34
|
const langCode = currentLanguage || "en-US";
|
|
38
35
|
const currentLanguageData = React.useMemo(() => getLanguageByCode(langCode), [langCode]);
|
package/src/account.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @umituz/react-native-settings/account
|
|
3
|
-
*
|
|
4
|
-
* NOTE: Auth has been removed from this application.
|
|
5
|
-
* This file provides empty exports for compatibility.
|
|
6
|
-
*
|
|
7
|
-
* Apps that use @umituz/react-native-auth should import directly from that package:
|
|
8
|
-
* import { AccountScreen, ProfileSection } from '@umituz/react-native-auth';
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// Empty exports
|
|
12
|
-
export const AccountScreen: React.ComponentType<Record<string, never>> | null = null;
|
|
13
|
-
export const ProfileSection: React.ComponentType<Record<string, never>> | null = null;
|
|
14
|
-
|
|
15
|
-
// Stub hooks that return default values
|
|
16
|
-
export const useAuth = () => ({
|
|
17
|
-
user: null,
|
|
18
|
-
loading: false,
|
|
19
|
-
isAuthReady: true,
|
|
20
|
-
isAnonymous: true,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
export const useUserProfile = () => null;
|
|
24
|
-
|
|
25
|
-
export const useAuthHandlers = () => ({
|
|
26
|
-
handleRatePress: async () => {},
|
|
27
|
-
handleSignOut: async () => {},
|
|
28
|
-
handleDeleteAccount: async () => {},
|
|
29
|
-
handleSignIn: async () => {},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Empty types
|
|
33
|
-
export type AccountScreenConfig = Record<string, never>;
|
|
34
|
-
|
|
35
|
-
// Base hook (no auth version)
|
|
36
|
-
export { useSettingsScreenConfig } from './presentation/hooks/useSettingsScreenConfig';
|
|
37
|
-
|
|
38
|
-
// Re-export types
|
|
39
|
-
export type {
|
|
40
|
-
UseSettingsScreenConfigParams,
|
|
41
|
-
SettingsScreenConfigResult,
|
|
42
|
-
SettingsFeatures,
|
|
43
|
-
} from './presentation/hooks/useSettingsScreenConfig';
|
|
44
|
-
|
|
45
|
-
export type { AccountConfig } from './presentation/navigation/types';
|