@umituz/react-native-settings 4.21.10 → 4.21.12
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 +11 -2
- package/src/domains/gamification/components/GamificationScreenWrapper.tsx +42 -87
- package/src/domains/gamification/components/index.ts +1 -0
- package/src/domains/gamification/index.ts +3 -11
- package/src/index.ts +3 -0
- package/src/notifications/domains/quietHours/infrastructure/hooks/useQuietHoursActions.ts +52 -0
- package/src/notifications/domains/quietHours/presentation/components/QuietHoursCard.tsx +112 -0
- package/src/notifications/domains/reminders/infrastructure/config/reminderPresets.ts +120 -0
- package/src/notifications/domains/reminders/infrastructure/hooks/useReminderActions.ts +106 -0
- package/src/notifications/domains/reminders/infrastructure/storage/RemindersStore.ts +148 -0
- package/src/notifications/domains/reminders/presentation/components/FormButton.tsx +66 -0
- package/src/notifications/domains/reminders/presentation/components/FrequencySelector.tsx +72 -0
- package/src/notifications/domains/reminders/presentation/components/ReminderForm.tsx +169 -0
- package/src/notifications/domains/reminders/presentation/components/ReminderItem.tsx +130 -0
- package/src/notifications/domains/reminders/presentation/components/TimePresetSelector.tsx +100 -0
- package/src/notifications/domains/reminders/presentation/components/WeekdaySelector.tsx +61 -0
- package/src/notifications/domains/reminders/presentation/screens/ReminderListScreen.tsx +131 -0
- package/src/notifications/index.ts +139 -0
- package/src/notifications/infrastructure/config/notificationsConfig.ts +98 -0
- package/src/notifications/infrastructure/hooks/useNotificationSettings.ts +37 -0
- package/src/notifications/infrastructure/services/NotificationBadgeManager.ts +28 -0
- package/src/notifications/infrastructure/services/NotificationManager.ts +138 -0
- package/src/notifications/infrastructure/services/NotificationPermissions.ts +80 -0
- package/src/notifications/infrastructure/services/NotificationScheduler.ts +77 -0
- package/src/notifications/infrastructure/services/NotificationService.ts +50 -0
- package/src/notifications/infrastructure/services/types.ts +176 -0
- package/src/notifications/infrastructure/storage/NotificationsStore.ts +45 -0
- package/src/notifications/infrastructure/utils/dev.ts +25 -0
- package/src/notifications/infrastructure/utils/idGenerator.ts +14 -0
- package/src/notifications/infrastructure/utils/triggerBuilder.ts +45 -0
- package/src/notifications/presentation/components/NotificationsSection.tsx +84 -0
- package/src/notifications/presentation/components/RemindersNavRow.styles.ts +38 -0
- package/src/notifications/presentation/components/RemindersNavRow.tsx +51 -0
- package/src/notifications/presentation/components/SettingRow.tsx +86 -0
- package/src/notifications/presentation/hooks/useNotificationSettingsUI.ts +52 -0
- package/src/notifications/presentation/hooks/useTimePicker.ts +71 -0
- package/src/notifications/presentation/screens/NotificationSettingsScreen.styles.ts +30 -0
- package/src/notifications/presentation/screens/NotificationSettingsScreen.tsx +131 -0
- package/src/notifications/presentation/screens/NotificationsScreen.tsx +107 -0
- package/src/presentation/navigation/SettingsStackNavigator.tsx +21 -11
- package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +1 -1
- package/src/domains/gamification/README.md +0 -343
- package/src/domains/gamification/components/GamificationSettingsItem.tsx +0 -33
- package/src/domains/gamification/examples/gamification.config.example.ts +0 -70
- package/src/domains/gamification/examples/localization.example.json +0 -71
- package/src/domains/gamification/types/settings.ts +0 -28
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setting Row Component
|
|
3
|
+
* Reusable toggle row for settings
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useMemo, useCallback } from 'react';
|
|
7
|
+
import { View, StyleSheet, Switch } from 'react-native';
|
|
8
|
+
import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
9
|
+
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
10
|
+
|
|
11
|
+
export interface SettingRowProps {
|
|
12
|
+
iconName: string;
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
value: boolean;
|
|
16
|
+
onToggle: (value: boolean) => void;
|
|
17
|
+
onHapticFeedback?: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const SettingRow: React.FC<SettingRowProps> = ({
|
|
21
|
+
iconName,
|
|
22
|
+
title,
|
|
23
|
+
description,
|
|
24
|
+
value,
|
|
25
|
+
onToggle,
|
|
26
|
+
onHapticFeedback,
|
|
27
|
+
}) => {
|
|
28
|
+
const tokens = useAppDesignTokens();
|
|
29
|
+
const styles = useMemo(() => createStyles(tokens), [tokens]);
|
|
30
|
+
|
|
31
|
+
const handleToggle = useCallback((newValue: boolean) => {
|
|
32
|
+
onHapticFeedback?.();
|
|
33
|
+
onToggle(newValue);
|
|
34
|
+
}, [onToggle, onHapticFeedback]);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<View style={styles.container}>
|
|
38
|
+
<View style={styles.iconContainer}>
|
|
39
|
+
<AtomicIcon name={iconName} size="md" color="primary" />
|
|
40
|
+
</View>
|
|
41
|
+
<View style={styles.textContainer}>
|
|
42
|
+
<AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
|
|
43
|
+
{title}
|
|
44
|
+
</AtomicText>
|
|
45
|
+
<AtomicText type="bodySmall" style={styles.description}>
|
|
46
|
+
{description}
|
|
47
|
+
</AtomicText>
|
|
48
|
+
</View>
|
|
49
|
+
<Switch
|
|
50
|
+
value={value}
|
|
51
|
+
onValueChange={handleToggle}
|
|
52
|
+
trackColor={{
|
|
53
|
+
false: tokens.colors.surfaceSecondary,
|
|
54
|
+
true: tokens.colors.primary
|
|
55
|
+
}}
|
|
56
|
+
thumbColor={tokens.colors.surface}
|
|
57
|
+
ios_backgroundColor={tokens.colors.surfaceSecondary}
|
|
58
|
+
/>
|
|
59
|
+
</View>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
|
|
64
|
+
StyleSheet.create({
|
|
65
|
+
container: {
|
|
66
|
+
flexDirection: 'row',
|
|
67
|
+
alignItems: 'center',
|
|
68
|
+
},
|
|
69
|
+
iconContainer: {
|
|
70
|
+
width: 44,
|
|
71
|
+
height: 44,
|
|
72
|
+
borderRadius: 22,
|
|
73
|
+
backgroundColor: tokens.colors.surfaceSecondary,
|
|
74
|
+
justifyContent: 'center',
|
|
75
|
+
alignItems: 'center',
|
|
76
|
+
marginRight: 12,
|
|
77
|
+
},
|
|
78
|
+
textContainer: {
|
|
79
|
+
flex: 1,
|
|
80
|
+
marginRight: 12,
|
|
81
|
+
},
|
|
82
|
+
description: {
|
|
83
|
+
color: tokens.colors.textSecondary,
|
|
84
|
+
marginTop: 2,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useNotificationSettingsUI Hook
|
|
3
|
+
* Handles all business logic for notification settings screen
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useEffect, useCallback } from 'react';
|
|
7
|
+
import { useNotificationPreferences, useQuietHours, usePreferencesStore, useRemindersLoading } from '../../domains/reminders/infrastructure/storage/RemindersStore';
|
|
8
|
+
import { notificationService } from '../../infrastructure/services/NotificationService';
|
|
9
|
+
|
|
10
|
+
export const useNotificationSettingsUI = () => {
|
|
11
|
+
const preferences = useNotificationPreferences();
|
|
12
|
+
const quietHours = useQuietHours();
|
|
13
|
+
const { initialize, updatePreferences, updateQuietHours } = usePreferencesStore();
|
|
14
|
+
const isLoading = useRemindersLoading();
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
initialize();
|
|
18
|
+
}, [initialize]);
|
|
19
|
+
|
|
20
|
+
const handleMasterToggle = useCallback(async (value: boolean) => {
|
|
21
|
+
if (value) {
|
|
22
|
+
const hasPermission = await notificationService.hasPermissions();
|
|
23
|
+
if (!hasPermission) {
|
|
24
|
+
const granted = await notificationService.requestPermissions();
|
|
25
|
+
if (!granted) return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await updatePreferences({ enabled: value });
|
|
29
|
+
}, [updatePreferences]);
|
|
30
|
+
|
|
31
|
+
const handleSoundToggle = useCallback(async (value: boolean) => {
|
|
32
|
+
await updatePreferences({ sound: value });
|
|
33
|
+
}, [updatePreferences]);
|
|
34
|
+
|
|
35
|
+
const handleVibrationToggle = useCallback(async (value: boolean) => {
|
|
36
|
+
await updatePreferences({ vibration: value });
|
|
37
|
+
}, [updatePreferences]);
|
|
38
|
+
|
|
39
|
+
const handleQuietHoursToggle = useCallback(async (value: boolean) => {
|
|
40
|
+
await updateQuietHours({ ...quietHours, enabled: value });
|
|
41
|
+
}, [quietHours, updateQuietHours]);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
preferences,
|
|
45
|
+
quietHours,
|
|
46
|
+
isLoading,
|
|
47
|
+
handleMasterToggle,
|
|
48
|
+
handleSoundToggle,
|
|
49
|
+
handleVibrationToggle,
|
|
50
|
+
handleQuietHoursToggle,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useTimePicker Hook
|
|
3
|
+
* Encapsulates DateTimePicker logic for notification settings
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback } from 'react';
|
|
7
|
+
import type { QuietHoursConfig } from '../../infrastructure/services/types';
|
|
8
|
+
|
|
9
|
+
export type PickerMode = 'start' | 'end' | null;
|
|
10
|
+
|
|
11
|
+
export interface UseTimePickerParams {
|
|
12
|
+
quietHours: QuietHoursConfig;
|
|
13
|
+
onStartTimeChange: (hours: number, minutes: number) => void;
|
|
14
|
+
onEndTimeChange: (hours: number, minutes: number) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TimePickerHandlers {
|
|
18
|
+
pickerMode: PickerMode;
|
|
19
|
+
handleStartTimePress: () => void;
|
|
20
|
+
handleEndTimePress: () => void;
|
|
21
|
+
handleTimeChange: (event: any, selectedDate?: Date) => void;
|
|
22
|
+
getPickerDate: () => Date;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const useTimePicker = ({
|
|
26
|
+
quietHours,
|
|
27
|
+
onStartTimeChange,
|
|
28
|
+
onEndTimeChange,
|
|
29
|
+
}: UseTimePickerParams): TimePickerHandlers => {
|
|
30
|
+
const [pickerMode, setPickerMode] = useState<PickerMode>(null);
|
|
31
|
+
|
|
32
|
+
const handleStartTimePress = useCallback(() => {
|
|
33
|
+
setPickerMode('start');
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
const handleEndTimePress = useCallback(() => {
|
|
37
|
+
setPickerMode('end');
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
const handleTimeChange = useCallback((event: any, selectedDate?: Date) => {
|
|
41
|
+
if (event.type === 'set' && selectedDate) {
|
|
42
|
+
const hours = selectedDate.getHours();
|
|
43
|
+
const minutes = selectedDate.getMinutes();
|
|
44
|
+
|
|
45
|
+
if (pickerMode === 'start') {
|
|
46
|
+
onStartTimeChange(hours, minutes);
|
|
47
|
+
} else if (pickerMode === 'end') {
|
|
48
|
+
onEndTimeChange(hours, minutes);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
setPickerMode(null);
|
|
52
|
+
}, [pickerMode, onStartTimeChange, onEndTimeChange]);
|
|
53
|
+
|
|
54
|
+
const getPickerDate = useCallback((): Date => {
|
|
55
|
+
const date = new Date();
|
|
56
|
+
if (pickerMode === 'start') {
|
|
57
|
+
date.setHours(quietHours.startHour, quietHours.startMinute);
|
|
58
|
+
} else if (pickerMode === 'end') {
|
|
59
|
+
date.setHours(quietHours.endHour, quietHours.endMinute);
|
|
60
|
+
}
|
|
61
|
+
return date;
|
|
62
|
+
}, [pickerMode, quietHours]);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
pickerMode,
|
|
66
|
+
handleStartTimePress,
|
|
67
|
+
handleEndTimePress,
|
|
68
|
+
handleTimeChange,
|
|
69
|
+
getPickerDate,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NotificationSettingsScreen Styles
|
|
3
|
+
* Extracted styles for better organization and maintainability
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { StyleSheet } from 'react-native';
|
|
7
|
+
import type { DesignTokens } from '@umituz/react-native-design-system';
|
|
8
|
+
|
|
9
|
+
export const createStyles = (tokens: DesignTokens) =>
|
|
10
|
+
StyleSheet.create({
|
|
11
|
+
container: {
|
|
12
|
+
flex: 1,
|
|
13
|
+
padding: 16,
|
|
14
|
+
},
|
|
15
|
+
loadingContainer: {
|
|
16
|
+
flex: 1,
|
|
17
|
+
justifyContent: 'center',
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
},
|
|
20
|
+
card: {
|
|
21
|
+
marginBottom: 16,
|
|
22
|
+
padding: 16,
|
|
23
|
+
backgroundColor: tokens.colors.surface,
|
|
24
|
+
},
|
|
25
|
+
divider: {
|
|
26
|
+
height: 1,
|
|
27
|
+
backgroundColor: tokens.colors.surfaceSecondary,
|
|
28
|
+
marginVertical: 12,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NotificationSettingsScreen
|
|
3
|
+
* Clean presentation-only screen for notification settings
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { View } from 'react-native';
|
|
8
|
+
import { AtomicCard, ScreenLayout, AtomicSpinner } from '@umituz/react-native-design-system';
|
|
9
|
+
import { QuietHoursCard } from '../../domains/quietHours/presentation/components/QuietHoursCard';
|
|
10
|
+
import { SettingRow } from '../components/SettingRow';
|
|
11
|
+
import { RemindersNavRow } from '../components/RemindersNavRow';
|
|
12
|
+
import { useNotificationSettingsUI } from '../hooks/useNotificationSettingsUI';
|
|
13
|
+
import { useTimePicker } from '../hooks/useTimePicker';
|
|
14
|
+
import { useReminders } from '../../domains/reminders/infrastructure/storage/RemindersStore';
|
|
15
|
+
import { useQuietHoursActions } from '../../domains/quietHours/infrastructure/hooks/useQuietHoursActions';
|
|
16
|
+
import type { NotificationSettingsTranslations, QuietHoursTranslations } from '../../infrastructure/services/types';
|
|
17
|
+
import { createStyles } from './NotificationSettingsScreen.styles';
|
|
18
|
+
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
19
|
+
// @ts-ignore - Optional peer dependency
|
|
20
|
+
import DateTimePicker from '@react-native-community/datetimepicker';
|
|
21
|
+
|
|
22
|
+
export interface NotificationSettingsScreenProps {
|
|
23
|
+
translations: NotificationSettingsTranslations;
|
|
24
|
+
quietHoursTranslations: QuietHoursTranslations;
|
|
25
|
+
onHapticFeedback?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProps> = ({
|
|
29
|
+
translations,
|
|
30
|
+
quietHoursTranslations,
|
|
31
|
+
onHapticFeedback,
|
|
32
|
+
}) => {
|
|
33
|
+
const tokens = useAppDesignTokens();
|
|
34
|
+
const styles = createStyles(tokens);
|
|
35
|
+
const reminders = useReminders();
|
|
36
|
+
const { setStartTime, setEndTime } = useQuietHoursActions();
|
|
37
|
+
|
|
38
|
+
const {
|
|
39
|
+
preferences,
|
|
40
|
+
quietHours,
|
|
41
|
+
isLoading,
|
|
42
|
+
handleMasterToggle,
|
|
43
|
+
handleSoundToggle,
|
|
44
|
+
handleVibrationToggle,
|
|
45
|
+
handleQuietHoursToggle,
|
|
46
|
+
} = useNotificationSettingsUI();
|
|
47
|
+
|
|
48
|
+
const timePicker = useTimePicker({
|
|
49
|
+
quietHours,
|
|
50
|
+
onStartTimeChange: setStartTime,
|
|
51
|
+
onEndTimeChange: setEndTime,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const handleRemindersPress = () => {
|
|
55
|
+
// Navigate to reminders screen when implemented
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
if (isLoading) {
|
|
59
|
+
return (
|
|
60
|
+
<ScreenLayout>
|
|
61
|
+
<AtomicSpinner size="lg" color="primary" fullContainer />
|
|
62
|
+
</ScreenLayout>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<ScreenLayout hideScrollIndicator>
|
|
68
|
+
<View style={styles.container}>
|
|
69
|
+
<AtomicCard style={styles.card}>
|
|
70
|
+
<SettingRow
|
|
71
|
+
iconName="notifications"
|
|
72
|
+
title={translations.masterToggleTitle}
|
|
73
|
+
description={translations.masterToggleDescription}
|
|
74
|
+
value={preferences.enabled}
|
|
75
|
+
onToggle={handleMasterToggle}
|
|
76
|
+
onHapticFeedback={onHapticFeedback}
|
|
77
|
+
/>
|
|
78
|
+
</AtomicCard>
|
|
79
|
+
|
|
80
|
+
{preferences.enabled && (
|
|
81
|
+
<>
|
|
82
|
+
<AtomicCard style={styles.card}>
|
|
83
|
+
<SettingRow
|
|
84
|
+
iconName="volume-high"
|
|
85
|
+
title={translations.soundTitle}
|
|
86
|
+
description={translations.soundDescription}
|
|
87
|
+
value={preferences.sound}
|
|
88
|
+
onToggle={handleSoundToggle}
|
|
89
|
+
onHapticFeedback={onHapticFeedback}
|
|
90
|
+
/>
|
|
91
|
+
<View style={styles.divider} />
|
|
92
|
+
<SettingRow
|
|
93
|
+
iconName="phone-portrait"
|
|
94
|
+
title={translations.vibrationTitle}
|
|
95
|
+
description={translations.vibrationDescription}
|
|
96
|
+
value={preferences.vibration}
|
|
97
|
+
onToggle={handleVibrationToggle}
|
|
98
|
+
onHapticFeedback={onHapticFeedback}
|
|
99
|
+
/>
|
|
100
|
+
</AtomicCard>
|
|
101
|
+
|
|
102
|
+
<AtomicCard style={styles.card}>
|
|
103
|
+
<RemindersNavRow
|
|
104
|
+
title={translations.remindersTitle}
|
|
105
|
+
description={translations.remindersDescription}
|
|
106
|
+
count={reminders.length}
|
|
107
|
+
onPress={handleRemindersPress}
|
|
108
|
+
/>
|
|
109
|
+
</AtomicCard>
|
|
110
|
+
|
|
111
|
+
<QuietHoursCard
|
|
112
|
+
config={quietHours}
|
|
113
|
+
translations={quietHoursTranslations}
|
|
114
|
+
onToggle={handleQuietHoursToggle}
|
|
115
|
+
onStartTimePress={timePicker.handleStartTimePress}
|
|
116
|
+
onEndTimePress={timePicker.handleEndTimePress}
|
|
117
|
+
/>
|
|
118
|
+
</>
|
|
119
|
+
)}
|
|
120
|
+
</View>
|
|
121
|
+
{timePicker.pickerMode && (
|
|
122
|
+
<DateTimePicker
|
|
123
|
+
value={timePicker.getPickerDate()}
|
|
124
|
+
mode="time"
|
|
125
|
+
is24Hour={true}
|
|
126
|
+
onChange={timePicker.handleTimeChange}
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
</ScreenLayout>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notifications Screen - Dynamic and Reusable
|
|
3
|
+
*
|
|
4
|
+
* A clean notification settings screen that accepts all text and configuration
|
|
5
|
+
* as props to make it completely reusable across different applications.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useMemo } from 'react';
|
|
9
|
+
import { View, StyleSheet } from 'react-native';
|
|
10
|
+
import { AtomicIcon, AtomicCard, AtomicText, ScreenLayout, STATIC_TOKENS, AtomicSpinner } from '@umituz/react-native-design-system';
|
|
11
|
+
import { Switch } from 'react-native';
|
|
12
|
+
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
13
|
+
import { useNotificationSettings } from '../../infrastructure/hooks/useNotificationSettings';
|
|
14
|
+
import type { DesignTokens } from '@umituz/react-native-design-system';
|
|
15
|
+
|
|
16
|
+
export interface NotificationsScreenProps {
|
|
17
|
+
translations: {
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
loadingText?: string;
|
|
21
|
+
};
|
|
22
|
+
iconName?: string;
|
|
23
|
+
iconColor?: string;
|
|
24
|
+
testID?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const NotificationsScreen: React.FC<NotificationsScreenProps> = ({
|
|
28
|
+
translations,
|
|
29
|
+
iconName = 'notifications',
|
|
30
|
+
iconColor = 'primary',
|
|
31
|
+
testID = 'notifications-screen',
|
|
32
|
+
}) => {
|
|
33
|
+
const tokens = useAppDesignTokens();
|
|
34
|
+
const styles = useMemo(() => getStyles(tokens), [tokens]);
|
|
35
|
+
const { notificationsEnabled, setNotificationsEnabled, isLoading } = useNotificationSettings();
|
|
36
|
+
|
|
37
|
+
if (isLoading) {
|
|
38
|
+
return (
|
|
39
|
+
<ScreenLayout testID={testID}>
|
|
40
|
+
<AtomicSpinner
|
|
41
|
+
size="lg"
|
|
42
|
+
color="primary"
|
|
43
|
+
text={translations.loadingText || 'Loading...'}
|
|
44
|
+
fullContainer
|
|
45
|
+
/>
|
|
46
|
+
</ScreenLayout>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<ScreenLayout testID={testID} hideScrollIndicator>
|
|
52
|
+
<AtomicCard style={styles.card}>
|
|
53
|
+
<View style={styles.settingItem}>
|
|
54
|
+
<View style={styles.iconContainer}>
|
|
55
|
+
<AtomicIcon name={iconName} size="lg" color={iconColor as any} />
|
|
56
|
+
</View>
|
|
57
|
+
<View style={styles.textContainer}>
|
|
58
|
+
<AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
|
|
59
|
+
{translations.title}
|
|
60
|
+
</AtomicText>
|
|
61
|
+
<AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary, marginTop: STATIC_TOKENS.spacing.xs }}>
|
|
62
|
+
{translations.description}
|
|
63
|
+
</AtomicText>
|
|
64
|
+
</View>
|
|
65
|
+
<Switch
|
|
66
|
+
value={notificationsEnabled}
|
|
67
|
+
onValueChange={setNotificationsEnabled}
|
|
68
|
+
trackColor={{ false: tokens.colors.surfaceSecondary, true: tokens.colors.primary }}
|
|
69
|
+
thumbColor={tokens.colors.surface}
|
|
70
|
+
testID="notifications-toggle"
|
|
71
|
+
/>
|
|
72
|
+
</View>
|
|
73
|
+
</AtomicCard>
|
|
74
|
+
</ScreenLayout>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const getStyles = (tokens: DesignTokens) => StyleSheet.create({
|
|
79
|
+
loadingContainer: {
|
|
80
|
+
flex: 1,
|
|
81
|
+
justifyContent: 'center',
|
|
82
|
+
alignItems: 'center',
|
|
83
|
+
},
|
|
84
|
+
card: {
|
|
85
|
+
padding: STATIC_TOKENS.spacing.lg,
|
|
86
|
+
backgroundColor: tokens.colors.surface,
|
|
87
|
+
},
|
|
88
|
+
settingItem: {
|
|
89
|
+
flexDirection: 'row',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
},
|
|
92
|
+
iconContainer: {
|
|
93
|
+
width: 48,
|
|
94
|
+
height: 48,
|
|
95
|
+
borderRadius: 24,
|
|
96
|
+
backgroundColor: tokens.colors.surfaceSecondary,
|
|
97
|
+
justifyContent: 'center',
|
|
98
|
+
alignItems: 'center',
|
|
99
|
+
marginRight: STATIC_TOKENS.spacing.md,
|
|
100
|
+
},
|
|
101
|
+
textContainer: {
|
|
102
|
+
flex: 1,
|
|
103
|
+
marginRight: STATIC_TOKENS.spacing.md,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export default NotificationsScreen;
|
|
@@ -8,9 +8,13 @@
|
|
|
8
8
|
import React from "react";
|
|
9
9
|
import { createStackNavigator } from "@react-navigation/stack";
|
|
10
10
|
import { useLocalization, LanguageSelectionScreen } from "@umituz/react-native-localization";
|
|
11
|
-
import { NotificationSettingsScreen } from "@
|
|
11
|
+
import { NotificationSettingsScreen } from "@notifications";
|
|
12
12
|
import { AccountScreen } from "@umituz/react-native-auth";
|
|
13
|
-
import { useAppDesignTokens
|
|
13
|
+
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
14
|
+
|
|
15
|
+
// ...
|
|
16
|
+
|
|
17
|
+
|
|
14
18
|
import { AppearanceScreen } from "../screens/AppearanceScreen";
|
|
15
19
|
import { FAQScreen } from "../../domains/faqs";
|
|
16
20
|
import { useNavigationHandlers } from "./hooks";
|
|
@@ -49,15 +53,21 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
|
|
|
49
53
|
useNavigationHandlers(appInfo, legalUrls);
|
|
50
54
|
|
|
51
55
|
const screenOptions = React.useMemo(
|
|
52
|
-
() =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
() => ({
|
|
57
|
+
headerStyle: {
|
|
58
|
+
backgroundColor: tokens.colors.surface,
|
|
59
|
+
borderBottomColor: tokens.colors.borderLight,
|
|
60
|
+
borderBottomWidth: 1,
|
|
61
|
+
elevation: 0,
|
|
62
|
+
shadowOpacity: 0,
|
|
63
|
+
},
|
|
64
|
+
headerTintColor: tokens.colors.textPrimary,
|
|
65
|
+
headerTitleStyle: {
|
|
66
|
+
color: tokens.colors.textPrimary,
|
|
67
|
+
fontWeight: "600" as const,
|
|
68
|
+
},
|
|
69
|
+
headerBackTitle: t("settings.title"),
|
|
70
|
+
}),
|
|
61
71
|
[tokens, t]
|
|
62
72
|
);
|
|
63
73
|
const notificationTranslations = React.useMemo(() => createNotificationTranslations(t), [t]);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { useNavigation } from "@react-navigation/native";
|
|
3
3
|
import { AppearanceSection } from "../../../../domains/appearance/presentation/components/AppearanceSection";
|
|
4
|
-
import { NotificationsSection } from "@
|
|
4
|
+
import { NotificationsSection } from "@notifications";
|
|
5
5
|
import { useLocalization, getLanguageByCode } from "@umituz/react-native-localization";
|
|
6
6
|
import { SettingsItemCard } from "../../../components/SettingsItemCard";
|
|
7
7
|
import type { NormalizedConfig } from "../../utils/normalizeConfig";
|