@umituz/react-native-settings 4.23.62 → 4.23.64
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 +2 -2
- package/src/domains/appearance/hooks/useAppearanceActions.ts +6 -4
- package/src/domains/faqs/domain/services/FAQSearchService.ts +2 -2
- package/src/domains/faqs/presentation/hooks/useFAQExpansion.ts +5 -3
- package/src/domains/feedback/domain/entities/FeedbackEntity.ts +0 -33
- package/src/domains/gamification/utils/calculations.ts +2 -3
- package/src/domains/legal/domain/services/ContentValidationService.ts +0 -50
- package/src/domains/legal/presentation/components/LegalLinks.tsx +0 -13
- package/src/domains/legal/presentation/screens/LegalContentScreen.tsx +0 -11
- package/src/domains/localization/infrastructure/components/LanguageSwitcher.tsx +0 -1
- package/src/domains/localization/infrastructure/components/useLanguageNavigation.ts +0 -1
- package/src/domains/localization/infrastructure/storage/LanguageInitializer.ts +1 -1
- package/src/domains/localization/infrastructure/storage/LanguageSwitcher.ts +1 -1
- package/src/domains/localization/presentation/components/LanguageItem.tsx +0 -1
- package/src/domains/localization/presentation/components/LanguageSection.tsx +0 -1
- package/src/domains/localization/presentation/screens/LanguageSelectionScreen.tsx +0 -1
- package/src/domains/notifications/infrastructure/services/NotificationScheduler.ts +2 -2
- package/src/domains/notifications/infrastructure/utils/triggerBuilder.ts +2 -2
- package/src/domains/notifications/presentation/screens/NotificationSettingsScreen.tsx +0 -1
- package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +5 -5
- package/src/domains/notifications/reminders/infrastructure/config/reminderPresets.ts +1 -1
- package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +25 -17
- package/src/domains/notifications/reminders/infrastructure/storage/RemindersStore.ts +7 -3
- package/src/index.ts +1 -1
- package/src/infrastructure/repositories/SettingsRepository.ts +1 -1
- package/src/infrastructure/services/SettingsService.ts +0 -6
- package/src/presentation/components/SettingsFooter.tsx +1 -1
- package/src/presentation/hooks/useSettingsScreenConfig.ts +3 -3
- package/src/presentation/navigation/SettingsStackNavigator.tsx +2 -2
- package/src/presentation/navigation/components/wrappers/SettingsScreenWrapper.tsx +2 -2
- package/src/presentation/navigation/components/wrappers/index.ts +0 -2
- package/src/presentation/navigation/types.ts +2 -2
- package/src/presentation/navigation/utils/navigationTranslations.ts +1 -1
- package/src/presentation/screens/SettingsScreen.tsx +0 -1
- package/src/presentation/screens/components/GamificationSettingsItem.tsx +0 -1
- package/src/presentation/screens/components/SettingsContent.tsx +0 -2
- package/src/presentation/screens/components/SubscriptionSettingsItem.tsx +0 -1
- package/src/presentation/screens/components/WalletSettingsItem.tsx +0 -1
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.64",
|
|
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",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
77
77
|
"@typescript-eslint/parser": "^7.18.0",
|
|
78
78
|
"@umituz/react-native-auth": "^3.6.49",
|
|
79
|
-
"@umituz/react-native-design-system": "^4.23.
|
|
79
|
+
"@umituz/react-native-design-system": "^4.23.58",
|
|
80
80
|
"@umituz/react-native-firebase": "^1.13.102",
|
|
81
81
|
"@umituz/react-native-sentry": "*",
|
|
82
82
|
"eslint": "^8.57.0",
|
|
@@ -17,10 +17,12 @@ export const useAppearanceActions = () => {
|
|
|
17
17
|
}, [setThemeMode]);
|
|
18
18
|
|
|
19
19
|
const handleColorChange = useCallback((key: keyof CustomThemeColors, color: string) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
setLocalCustomColors(prev => {
|
|
21
|
+
const newColors = { ...prev, [key]: color };
|
|
22
|
+
setCustomColors(newColors);
|
|
23
|
+
return newColors;
|
|
24
|
+
});
|
|
25
|
+
}, [setCustomColors]);
|
|
24
26
|
|
|
25
27
|
const handleResetColors = useCallback(() => {
|
|
26
28
|
reset();
|
|
@@ -50,10 +50,10 @@ export class FAQSearchService {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
static sortByOrder(items: FAQItem[]): FAQItem[] {
|
|
53
|
-
return [...items].sort((a, b) => (a.order
|
|
53
|
+
return [...items].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
static sortCategoriesByOrder(categories: FAQCategory[]): FAQCategory[] {
|
|
57
|
-
return [...categories].sort((a, b) => (a.order
|
|
57
|
+
return [...categories].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -23,11 +23,13 @@ export function useFAQExpansion(): UseFAQExpansionResult {
|
|
|
23
23
|
|
|
24
24
|
const toggleExpansion = useCallback((itemId: string) => {
|
|
25
25
|
setExpandedItems(prev => {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const next = new Set(prev);
|
|
27
|
+
if (next.has(itemId)) {
|
|
28
|
+
next.delete(itemId);
|
|
28
29
|
} else {
|
|
29
|
-
|
|
30
|
+
next.add(itemId);
|
|
30
31
|
}
|
|
32
|
+
return next;
|
|
31
33
|
});
|
|
32
34
|
}, []);
|
|
33
35
|
|
|
@@ -57,36 +57,3 @@ export function createFeedback(
|
|
|
57
57
|
updatedAt: now,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get feedback type label
|
|
63
|
-
* @deprecated Use translation keys instead: `feedback.type.${type}`
|
|
64
|
-
* This function is kept for backward compatibility but should not be used.
|
|
65
|
-
* Use t(`feedback.type.${type}`) from useLocalization() instead.
|
|
66
|
-
*/
|
|
67
|
-
export function getFeedbackTypeLabel(type: FeedbackType): string {
|
|
68
|
-
// Return type key for translation
|
|
69
|
-
return type;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function getFeedbackTypeEmoji(type: FeedbackType): string {
|
|
73
|
-
const emojis: Record<FeedbackType, string> = {
|
|
74
|
-
general: '💬',
|
|
75
|
-
bug_report: '🐛',
|
|
76
|
-
feature_request: '✨',
|
|
77
|
-
improvement: '🔧',
|
|
78
|
-
other: '📝',
|
|
79
|
-
};
|
|
80
|
-
return emojis[type] || '📝';
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function getFeedbackTypeIcon(type: FeedbackType): string {
|
|
84
|
-
const icons: Record<FeedbackType, string> = {
|
|
85
|
-
general: 'message-circle',
|
|
86
|
-
bug_report: 'bug',
|
|
87
|
-
feature_request: 'sparkles',
|
|
88
|
-
improvement: 'wrench',
|
|
89
|
-
other: 'file-text',
|
|
90
|
-
};
|
|
91
|
-
return icons[type] || 'file-text';
|
|
92
|
-
}
|
|
@@ -17,7 +17,7 @@ export const calculateLevel = (
|
|
|
17
17
|
for (let i = 0; i < sortedLevels.length; i++) {
|
|
18
18
|
if (points >= sortedLevels[i].minPoints) {
|
|
19
19
|
currentLevelDef = sortedLevels[i];
|
|
20
|
-
nextLevelDef = sortedLevels[i + 1]
|
|
20
|
+
nextLevelDef = sortedLevels[i + 1] ?? null;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -45,11 +45,10 @@ export const checkAchievementUnlock = (
|
|
|
45
45
|
): boolean => {
|
|
46
46
|
switch (definition.type) {
|
|
47
47
|
case "count":
|
|
48
|
+
case "milestone":
|
|
48
49
|
return tasksCompleted >= definition.threshold;
|
|
49
50
|
case "streak":
|
|
50
51
|
return currentStreak >= definition.threshold;
|
|
51
|
-
case "milestone":
|
|
52
|
-
return tasksCompleted >= definition.threshold;
|
|
53
52
|
default:
|
|
54
53
|
return false;
|
|
55
54
|
}
|
|
@@ -4,57 +4,7 @@
|
|
|
4
4
|
* Extracted from screens to follow SOLID principles
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export interface ContentValidationRule {
|
|
8
|
-
content?: string;
|
|
9
|
-
url?: string;
|
|
10
|
-
title?: string;
|
|
11
|
-
viewOnlineText?: string;
|
|
12
|
-
openText?: string;
|
|
13
|
-
privacyText?: string;
|
|
14
|
-
termsText?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
7
|
export class ContentValidationService {
|
|
18
|
-
/**
|
|
19
|
-
* Validate screen content requirements
|
|
20
|
-
*/
|
|
21
|
-
static validateScreenContent(
|
|
22
|
-
_content: string | undefined,
|
|
23
|
-
_url: string | undefined,
|
|
24
|
-
_title: string | undefined,
|
|
25
|
-
_viewOnlineText: string | undefined,
|
|
26
|
-
_openText: string | undefined,
|
|
27
|
-
_screenName: string
|
|
28
|
-
): void {
|
|
29
|
-
// Silent validation - no console output
|
|
30
|
-
void _content;
|
|
31
|
-
void _url;
|
|
32
|
-
void _title;
|
|
33
|
-
void _viewOnlineText;
|
|
34
|
-
void _openText;
|
|
35
|
-
void _screenName;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Validate legal links requirements
|
|
40
|
-
*/
|
|
41
|
-
static validateLegalLinks(
|
|
42
|
-
_privacyPolicyUrl: string | undefined,
|
|
43
|
-
_termsOfServiceUrl: string | undefined,
|
|
44
|
-
_privacyText: string | undefined,
|
|
45
|
-
_termsText: string | undefined,
|
|
46
|
-
_onPrivacyPress: (() => void) | undefined,
|
|
47
|
-
_onTermsPress: (() => void) | undefined
|
|
48
|
-
): void {
|
|
49
|
-
// Silent validation - no console output
|
|
50
|
-
void _privacyPolicyUrl;
|
|
51
|
-
void _termsOfServiceUrl;
|
|
52
|
-
void _privacyText;
|
|
53
|
-
void _termsText;
|
|
54
|
-
void _onPrivacyPress;
|
|
55
|
-
void _onTermsPress;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
8
|
/**
|
|
59
9
|
* Check if content is valid
|
|
60
10
|
*/
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import React from "react";
|
|
8
8
|
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
9
9
|
import { AtomicText } from "@umituz/react-native-design-system";
|
|
10
|
-
import { ContentValidationService } from "../../domain/services/ContentValidationService";
|
|
11
10
|
import { UrlHandlerService } from "../../domain/services/UrlHandlerService";
|
|
12
11
|
|
|
13
12
|
export interface LegalLinksProps {
|
|
@@ -51,18 +50,6 @@ export const LegalLinks: React.FC<LegalLinksProps> = React.memo(
|
|
|
51
50
|
onTermsPress,
|
|
52
51
|
style,
|
|
53
52
|
}) => {
|
|
54
|
-
// Validate required props
|
|
55
|
-
React.useEffect(() => {
|
|
56
|
-
ContentValidationService.validateLegalLinks(
|
|
57
|
-
privacyPolicyUrl,
|
|
58
|
-
termsOfServiceUrl,
|
|
59
|
-
privacyText,
|
|
60
|
-
termsText,
|
|
61
|
-
onPrivacyPress,
|
|
62
|
-
onTermsPress
|
|
63
|
-
);
|
|
64
|
-
}, [privacyPolicyUrl, termsOfServiceUrl, privacyText, termsText, onPrivacyPress, onTermsPress]);
|
|
65
|
-
|
|
66
53
|
// Memoize press handlers to prevent child re-renders
|
|
67
54
|
const handlePrivacyPress = React.useCallback(async () => {
|
|
68
55
|
if (onPrivacyPress) {
|
|
@@ -37,17 +37,6 @@ export const LegalContentScreen: React.FC<LegalContentScreenProps> = React.memo(
|
|
|
37
37
|
const tokens = useAppDesignTokens();
|
|
38
38
|
const insets = useSafeAreaInsets();
|
|
39
39
|
|
|
40
|
-
React.useEffect(() => {
|
|
41
|
-
ContentValidationService.validateScreenContent(
|
|
42
|
-
content,
|
|
43
|
-
url,
|
|
44
|
-
title,
|
|
45
|
-
viewOnlineText,
|
|
46
|
-
openText,
|
|
47
|
-
styleCacheKey
|
|
48
|
-
);
|
|
49
|
-
}, [content, url, title, viewOnlineText, openText, styleCacheKey]);
|
|
50
|
-
|
|
51
40
|
const styles = React.useMemo(() => {
|
|
52
41
|
const cacheKey = StyleCacheService.createTokenCacheKey(tokens);
|
|
53
42
|
return StyleCacheService.getCachedStyles(
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import React, { useMemo } from 'react';
|
|
7
7
|
import { TouchableOpacity, type StyleProp, type ViewStyle, type TextStyle } from 'react-native';
|
|
8
|
-
// @ts-ignore - Optional peer dependency
|
|
9
8
|
import { useAppDesignTokens, AtomicText } from '@umituz/react-native-design-system';
|
|
10
9
|
import { useLanguageSwitcher } from './useLanguageSwitcher';
|
|
11
10
|
import { styles, DEFAULT_CONFIG_VALUES } from './LanguageSwitcher.styles';
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
type ViewStyle,
|
|
14
14
|
type TextStyle,
|
|
15
15
|
} from 'react-native';
|
|
16
|
-
// @ts-ignore - Optional peer dependency
|
|
17
16
|
import { useAppDesignTokens, AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
18
17
|
import type { Language } from '../../infrastructure/storage/types/Language';
|
|
19
18
|
import { styles } from './LanguageItem.styles';
|
|
@@ -67,8 +67,8 @@ export class NotificationScheduler {
|
|
|
67
67
|
return notifications.map(notification => ({
|
|
68
68
|
identifier: notification.identifier,
|
|
69
69
|
content: {
|
|
70
|
-
title: notification.content.title
|
|
71
|
-
body: notification.content.body
|
|
70
|
+
title: notification.content.title ?? '',
|
|
71
|
+
body: notification.content.body ?? '',
|
|
72
72
|
data: notification.content.data as Record<string, unknown>,
|
|
73
73
|
},
|
|
74
74
|
trigger: notification.trigger,
|
|
@@ -26,7 +26,7 @@ export const buildTrigger = (reminder: Reminder): NotificationTrigger => {
|
|
|
26
26
|
case 'weekly':
|
|
27
27
|
return {
|
|
28
28
|
type: 'weekly',
|
|
29
|
-
weekday: weekday
|
|
29
|
+
weekday: weekday ?? 1,
|
|
30
30
|
hour,
|
|
31
31
|
minute,
|
|
32
32
|
};
|
|
@@ -34,7 +34,7 @@ export const buildTrigger = (reminder: Reminder): NotificationTrigger => {
|
|
|
34
34
|
case 'monthly':
|
|
35
35
|
return {
|
|
36
36
|
type: 'monthly',
|
|
37
|
-
day: dayOfMonth
|
|
37
|
+
day: dayOfMonth ?? 1,
|
|
38
38
|
hour,
|
|
39
39
|
minute,
|
|
40
40
|
};
|
|
@@ -23,7 +23,6 @@ import { useReminders } from '../../reminders/infrastructure/storage/RemindersSt
|
|
|
23
23
|
import { useQuietHoursActions } from '../../quietHours/infrastructure/hooks/useQuietHoursActions';
|
|
24
24
|
import type { NotificationSettingsTranslations, QuietHoursTranslations } from '../../infrastructure/services/types';
|
|
25
25
|
import { createStyles } from './NotificationSettingsScreen.styles';
|
|
26
|
-
// @ts-ignore - Optional peer dependency
|
|
27
26
|
import DateTimePicker from '@react-native-community/datetimepicker';
|
|
28
27
|
|
|
29
28
|
export interface NotificationSettingsScreenProps {
|
|
@@ -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,
|
|
10
|
+
import { AtomicIcon, AtomicCard, AtomicText, ScreenLayout, BASE_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';
|
|
@@ -58,7 +58,7 @@ export const NotificationsScreen: React.FC<NotificationsScreenProps> = ({
|
|
|
58
58
|
<AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
|
|
59
59
|
{translations.title}
|
|
60
60
|
</AtomicText>
|
|
61
|
-
<AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary, marginTop:
|
|
61
|
+
<AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary, marginTop: BASE_TOKENS.spacing.xs }}>
|
|
62
62
|
{translations.description}
|
|
63
63
|
</AtomicText>
|
|
64
64
|
</View>
|
|
@@ -82,7 +82,7 @@ const getStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
|
82
82
|
alignItems: 'center',
|
|
83
83
|
},
|
|
84
84
|
card: {
|
|
85
|
-
padding:
|
|
85
|
+
padding: BASE_TOKENS.spacing.lg,
|
|
86
86
|
backgroundColor: tokens.colors.surface,
|
|
87
87
|
},
|
|
88
88
|
settingItem: {
|
|
@@ -96,11 +96,11 @@ const getStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
|
96
96
|
backgroundColor: tokens.colors.surfaceSecondary,
|
|
97
97
|
justifyContent: 'center',
|
|
98
98
|
alignItems: 'center',
|
|
99
|
-
marginRight:
|
|
99
|
+
marginRight: BASE_TOKENS.spacing.md,
|
|
100
100
|
},
|
|
101
101
|
textContainer: {
|
|
102
102
|
flex: 1,
|
|
103
|
-
marginRight:
|
|
103
|
+
marginRight: BASE_TOKENS.spacing.md,
|
|
104
104
|
},
|
|
105
105
|
});
|
|
106
106
|
|
|
@@ -116,5 +116,5 @@ export const formatTime = (hour: number, minute: number): string => {
|
|
|
116
116
|
|
|
117
117
|
export const parseTime = (timeString: string): { hour: number; minute: number } => {
|
|
118
118
|
const [hour, minute] = timeString.split(':').map(Number);
|
|
119
|
-
return { hour: hour
|
|
119
|
+
return { hour: hour ?? 0, minute: minute ?? 0 };
|
|
120
120
|
};
|
|
@@ -25,15 +25,19 @@ export const useReminderActions = () => {
|
|
|
25
25
|
updatedAt: now,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
try {
|
|
29
|
+
const trigger = buildTrigger(reminder);
|
|
30
|
+
const notificationId = await scheduler.scheduleNotification({
|
|
31
|
+
title: reminder.title,
|
|
32
|
+
body: reminder.body,
|
|
33
|
+
trigger,
|
|
34
|
+
data: { reminderId: reminder.id },
|
|
35
|
+
});
|
|
36
|
+
reminder.notificationId = notificationId;
|
|
37
|
+
} catch {
|
|
38
|
+
reminder.enabled = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
await addReminder(reminder);
|
|
38
42
|
|
|
39
43
|
return reminder;
|
|
@@ -86,14 +90,18 @@ export const useReminderActions = () => {
|
|
|
86
90
|
await scheduler.cancelNotification(reminder.notificationId);
|
|
87
91
|
await updateReminder(id, { enabled: false, notificationId: undefined });
|
|
88
92
|
} else if (!reminder.enabled) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
try {
|
|
94
|
+
const trigger = buildTrigger(reminder);
|
|
95
|
+
const notificationId = await scheduler.scheduleNotification({
|
|
96
|
+
title: reminder.title,
|
|
97
|
+
body: reminder.body,
|
|
98
|
+
trigger,
|
|
99
|
+
data: { reminderId: reminder.id },
|
|
100
|
+
});
|
|
101
|
+
await updateReminder(id, { enabled: true, notificationId });
|
|
102
|
+
} catch {
|
|
103
|
+
await updateReminder(id, { enabled: false });
|
|
104
|
+
}
|
|
97
105
|
}
|
|
98
106
|
}, [updateReminder]);
|
|
99
107
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Manages reminder state with AsyncStorage persistence
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { useMemo } from 'react';
|
|
6
7
|
import { createStore } from '@umituz/react-native-design-system';
|
|
7
8
|
import type { Reminder, QuietHoursConfig, NotificationPreferences } from '../../../infrastructure/services/types';
|
|
8
9
|
|
|
@@ -76,7 +77,7 @@ interface PreferencesState {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
interface PreferencesActions {
|
|
79
|
-
initialize: () =>
|
|
80
|
+
initialize: () => void;
|
|
80
81
|
updatePreferences: (updates: Partial<NotificationPreferences>) => void;
|
|
81
82
|
updateQuietHours: (quietHours: QuietHoursConfig) => void;
|
|
82
83
|
reset: () => void;
|
|
@@ -110,7 +111,7 @@ export const usePreferencesStore = createStore<PreferencesState, PreferencesActi
|
|
|
110
111
|
initialState: initialPreferencesState,
|
|
111
112
|
persist: true,
|
|
112
113
|
actions: (set, get) => ({
|
|
113
|
-
initialize:
|
|
114
|
+
initialize: () => {
|
|
114
115
|
set({ isLoading: false, isInitialized: true });
|
|
115
116
|
},
|
|
116
117
|
|
|
@@ -135,7 +136,10 @@ export const usePreferencesStore = createStore<PreferencesState, PreferencesActi
|
|
|
135
136
|
// ============================================================================
|
|
136
137
|
|
|
137
138
|
export const useReminders = () => useRemindersStore(state => state.reminders);
|
|
138
|
-
export const useEnabledReminders = () =>
|
|
139
|
+
export const useEnabledReminders = () => {
|
|
140
|
+
const reminders = useRemindersStore(state => state.reminders);
|
|
141
|
+
return useMemo(() => reminders.filter(r => r.enabled), [reminders]);
|
|
142
|
+
};
|
|
139
143
|
export const useReminderById = (id: string) => useRemindersStore(state => state.reminders.find(r => r.id === id));
|
|
140
144
|
|
|
141
145
|
// ============================================================================
|
package/src/index.ts
CHANGED
|
@@ -9,17 +9,11 @@ import type { UserSettings, SettingsResult } from '../../application/ports/ISett
|
|
|
9
9
|
|
|
10
10
|
export class SettingsService {
|
|
11
11
|
private repository: SettingsRepository;
|
|
12
|
-
private initialized: boolean = false;
|
|
13
12
|
|
|
14
13
|
constructor() {
|
|
15
14
|
this.repository = new SettingsRepository();
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
async initialize(): Promise<void> {
|
|
19
|
-
if (this.initialized) return;
|
|
20
|
-
this.initialized = true;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
17
|
async getSettings(userId: string): Promise<SettingsResult<UserSettings>> {
|
|
24
18
|
return this.repository.getSettings(userId);
|
|
25
19
|
}
|
|
@@ -17,7 +17,7 @@ export const SettingsFooter: React.FC<SettingsFooterProps> = ({
|
|
|
17
17
|
|
|
18
18
|
const displayText = versionText || (appVersion && versionLabel
|
|
19
19
|
? `${versionLabel} ${appVersion}`
|
|
20
|
-
: appVersion
|
|
20
|
+
: appVersion);
|
|
21
21
|
|
|
22
22
|
if (!displayText) return null;
|
|
23
23
|
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
} from "../utils/config-creators";
|
|
30
30
|
import type { SettingsConfig } from "../screens/types";
|
|
31
31
|
import type { FeedbackFormData } from "../utils/config-creators";
|
|
32
|
-
import type { AppInfo, FAQData,
|
|
32
|
+
import type { AppInfo, FAQData, UserProfileDisplay, AdditionalScreen } from "../navigation/types";
|
|
33
33
|
import type { AccountScreenConfig } from "@umituz/react-native-auth";
|
|
34
34
|
|
|
35
35
|
export interface SettingsFeatures {
|
|
@@ -54,7 +54,7 @@ export interface UseSettingsScreenConfigParams {
|
|
|
54
54
|
|
|
55
55
|
export interface SettingsScreenConfigResult {
|
|
56
56
|
settingsConfig: SettingsConfig;
|
|
57
|
-
userProfile:
|
|
57
|
+
userProfile: UserProfileDisplay;
|
|
58
58
|
accountConfig: AccountScreenConfig;
|
|
59
59
|
translatedFaqData: FAQData | undefined;
|
|
60
60
|
isLoading: boolean;
|
|
@@ -127,7 +127,7 @@ export const useSettingsScreenConfig = (
|
|
|
127
127
|
showAbout, showLegal, showFaqs, showRating,
|
|
128
128
|
]);
|
|
129
129
|
|
|
130
|
-
const userProfile = useMemo(():
|
|
130
|
+
const userProfile = useMemo((): UserProfileDisplay => {
|
|
131
131
|
const isAnonymous = userProfileData?.isAnonymous ?? true;
|
|
132
132
|
return {
|
|
133
133
|
displayName: userProfileData?.displayName || t("settings.profile.anonymousName"),
|
|
@@ -10,11 +10,11 @@ import { useAppDesignTokens, StackNavigator, type StackScreen, type StackNavigat
|
|
|
10
10
|
import { useLocalization, LanguageSelectionScreen } from "../../domains/localization";
|
|
11
11
|
import { NotificationSettingsScreen } from "../../domains/notifications";
|
|
12
12
|
import { AccountScreen } from "@umituz/react-native-auth";
|
|
13
|
+
import { SettingsScreen } from "../screens/SettingsScreen";
|
|
13
14
|
import { AppearanceScreen } from "../screens/AppearanceScreen";
|
|
14
15
|
import { FAQScreen } from "../../domains/faqs";
|
|
15
16
|
import { useNavigationHandlers } from "./hooks";
|
|
16
17
|
import {
|
|
17
|
-
SettingsScreenWrapper,
|
|
18
18
|
LegalScreenWrapper,
|
|
19
19
|
AboutScreenWrapper,
|
|
20
20
|
} from "./components/wrappers";
|
|
@@ -79,7 +79,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
|
|
|
79
79
|
name: "SettingsMain",
|
|
80
80
|
options: { headerShown: false },
|
|
81
81
|
children: () => (
|
|
82
|
-
<
|
|
82
|
+
<SettingsScreen
|
|
83
83
|
config={config}
|
|
84
84
|
appVersion={appInfo.version}
|
|
85
85
|
showUserProfile={showUserProfile}
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
import React from "react";
|
|
5
5
|
import { SettingsScreen } from "../../../screens/SettingsScreen";
|
|
6
6
|
import type { SettingsConfig, CustomSettingsSection } from "../../../screens/types";
|
|
7
|
-
import type {
|
|
7
|
+
import type { UserProfileDisplay } from "../../types";
|
|
8
8
|
import type { DevSettingsProps } from "../../../../domains/dev";
|
|
9
9
|
|
|
10
10
|
export interface SettingsScreenWrapperProps {
|
|
11
11
|
config?: SettingsConfig;
|
|
12
12
|
appVersion: string;
|
|
13
13
|
showUserProfile: boolean;
|
|
14
|
-
userProfile?:
|
|
14
|
+
userProfile?: UserProfileDisplay;
|
|
15
15
|
devSettings?: DevSettingsProps;
|
|
16
16
|
customSections?: CustomSettingsSection[];
|
|
17
17
|
showHeader?: boolean;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Wrapper Components
|
|
3
3
|
*/
|
|
4
|
-
export { SettingsScreenWrapper } from './SettingsScreenWrapper';
|
|
5
|
-
export type { SettingsScreenWrapperProps } from './SettingsScreenWrapper';
|
|
6
4
|
export { LegalScreenWrapper } from './LegalScreenWrapper';
|
|
7
5
|
export type { LegalScreenWrapperProps } from './LegalScreenWrapper';
|
|
8
6
|
export { AboutScreenWrapper } from './AboutScreenWrapper';
|
|
@@ -49,7 +49,7 @@ export type SettingsStackParamList = {
|
|
|
49
49
|
/**
|
|
50
50
|
* User profile configuration
|
|
51
51
|
*/
|
|
52
|
-
export interface
|
|
52
|
+
export interface UserProfileDisplay {
|
|
53
53
|
displayName?: string;
|
|
54
54
|
userId?: string;
|
|
55
55
|
isAnonymous?: boolean;
|
|
@@ -93,7 +93,7 @@ export interface SettingsStackNavigatorProps {
|
|
|
93
93
|
faqData?: FAQData;
|
|
94
94
|
config?: SettingsConfig;
|
|
95
95
|
showUserProfile?: boolean;
|
|
96
|
-
userProfile?:
|
|
96
|
+
userProfile?: UserProfileDisplay;
|
|
97
97
|
accountConfig?: AccountConfig;
|
|
98
98
|
additionalScreens?: AdditionalScreen[];
|
|
99
99
|
devSettings?: DevSettingsProps;
|
|
@@ -26,7 +26,7 @@ export const createQuietHoursTranslations = (t: (key: string) => string) => ({
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
export const createLegalScreenProps = (
|
|
29
|
-
t:
|
|
29
|
+
t: (key: string) => string,
|
|
30
30
|
handlePrivacyPress: () => void,
|
|
31
31
|
handleTermsPress: () => void,
|
|
32
32
|
handleEulaPress: () => void
|
|
@@ -92,7 +92,6 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
|
|
|
92
92
|
footerText={footerText}
|
|
93
93
|
appVersion={appVersion}
|
|
94
94
|
customSections={customSections}
|
|
95
|
-
showCloseButton={showCloseButton}
|
|
96
95
|
devSettings={devSettings}
|
|
97
96
|
gamificationConfig={gamificationConfig}
|
|
98
97
|
/>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
// @ts-ignore - Optional peer dependency
|
|
3
2
|
import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
4
3
|
import type { IconName } from "@umituz/react-native-design-system";
|
|
5
4
|
import { SettingsItemCard } from "../../components/SettingsItemCard";
|
|
@@ -50,7 +50,6 @@ interface SettingsContentProps {
|
|
|
50
50
|
footerText?: string;
|
|
51
51
|
appVersion?: string;
|
|
52
52
|
customSections?: CustomSettingsSection[];
|
|
53
|
-
showCloseButton?: boolean;
|
|
54
53
|
emptyStateText?: string;
|
|
55
54
|
devSettings?: DevSettingsProps;
|
|
56
55
|
gamificationConfig?: import("../../../domains/gamification").GamificationSettingsConfig;
|
|
@@ -65,7 +64,6 @@ export const SettingsContent: React.FC<SettingsContentProps> = ({
|
|
|
65
64
|
footerText,
|
|
66
65
|
appVersion,
|
|
67
66
|
customSections = [],
|
|
68
|
-
showCloseButton = false,
|
|
69
67
|
emptyStateText,
|
|
70
68
|
devSettings,
|
|
71
69
|
gamificationConfig,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
// @ts-ignore - Optional peer dependency
|
|
3
2
|
import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
4
3
|
import { SettingsItemCard } from "../../components/SettingsItemCard";
|
|
5
4
|
import type { IconName } from "@umituz/react-native-design-system";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
// @ts-ignore - Optional peer dependency
|
|
3
2
|
import { useAppNavigation } from "@umituz/react-native-design-system";
|
|
4
3
|
import { SettingsItemCard } from "../../components/SettingsItemCard";
|
|
5
4
|
import type { IconName } from "@umituz/react-native-design-system";
|