@umituz/react-native-settings 4.21.11 → 4.21.13

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.
Files changed (47) hide show
  1. package/package.json +11 -2
  2. package/src/domains/gamification/components/GamificationScreenWrapper.tsx +58 -85
  3. package/src/domains/gamification/components/index.ts +1 -0
  4. package/src/domains/gamification/index.ts +4 -11
  5. package/src/domains/gamification/types/index.ts +18 -0
  6. package/src/domains/notifications/index.ts +139 -0
  7. package/src/domains/notifications/infrastructure/config/notificationsConfig.ts +98 -0
  8. package/src/domains/notifications/infrastructure/hooks/useNotificationSettings.ts +37 -0
  9. package/src/domains/notifications/infrastructure/services/NotificationBadgeManager.ts +28 -0
  10. package/src/domains/notifications/infrastructure/services/NotificationManager.ts +138 -0
  11. package/src/domains/notifications/infrastructure/services/NotificationPermissions.ts +80 -0
  12. package/src/domains/notifications/infrastructure/services/NotificationScheduler.ts +77 -0
  13. package/src/domains/notifications/infrastructure/services/NotificationService.ts +50 -0
  14. package/src/domains/notifications/infrastructure/services/types.ts +176 -0
  15. package/src/domains/notifications/infrastructure/storage/NotificationsStore.ts +45 -0
  16. package/src/domains/notifications/infrastructure/utils/dev.ts +25 -0
  17. package/src/domains/notifications/infrastructure/utils/idGenerator.ts +14 -0
  18. package/src/domains/notifications/infrastructure/utils/triggerBuilder.ts +45 -0
  19. package/src/domains/notifications/presentation/components/NotificationsSection.tsx +84 -0
  20. package/src/domains/notifications/presentation/components/RemindersNavRow.styles.ts +38 -0
  21. package/src/domains/notifications/presentation/components/RemindersNavRow.tsx +51 -0
  22. package/src/domains/notifications/presentation/components/SettingRow.tsx +86 -0
  23. package/src/domains/notifications/presentation/hooks/useNotificationSettingsUI.ts +52 -0
  24. package/src/domains/notifications/presentation/hooks/useTimePicker.ts +71 -0
  25. package/src/domains/notifications/presentation/screens/NotificationSettingsScreen.styles.ts +30 -0
  26. package/src/domains/notifications/presentation/screens/NotificationSettingsScreen.tsx +131 -0
  27. package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +107 -0
  28. package/src/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.ts +52 -0
  29. package/src/domains/notifications/quietHours/presentation/components/QuietHoursCard.tsx +112 -0
  30. package/src/domains/notifications/reminders/infrastructure/config/reminderPresets.ts +120 -0
  31. package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +106 -0
  32. package/src/domains/notifications/reminders/infrastructure/storage/RemindersStore.ts +148 -0
  33. package/src/domains/notifications/reminders/presentation/components/FormButton.tsx +66 -0
  34. package/src/domains/notifications/reminders/presentation/components/FrequencySelector.tsx +72 -0
  35. package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +169 -0
  36. package/src/domains/notifications/reminders/presentation/components/ReminderItem.tsx +130 -0
  37. package/src/domains/notifications/reminders/presentation/components/TimePresetSelector.tsx +100 -0
  38. package/src/domains/notifications/reminders/presentation/components/WeekdaySelector.tsx +61 -0
  39. package/src/domains/notifications/reminders/presentation/screens/ReminderListScreen.tsx +131 -0
  40. package/src/index.ts +3 -0
  41. package/src/presentation/navigation/SettingsStackNavigator.tsx +21 -11
  42. package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +1 -1
  43. package/src/domains/gamification/README.md +0 -343
  44. package/src/domains/gamification/components/GamificationSettingsItem.tsx +0 -33
  45. package/src/domains/gamification/examples/gamification.config.example.ts +0 -70
  46. package/src/domains/gamification/examples/localization.example.json +0 -71
  47. package/src/domains/gamification/types/settings.ts +0 -28
@@ -0,0 +1,84 @@
1
+ /**
2
+ * NotificationsSection Component
3
+ * Settings section for notifications - navigates to full screen
4
+ */
5
+
6
+ import React, { useCallback, useMemo } from 'react';
7
+ import type { StyleProp, ViewStyle } from 'react-native';
8
+ import { View, TouchableOpacity, StyleSheet } from 'react-native';
9
+ import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
10
+ import { useAppDesignTokens } from '@umituz/react-native-design-system';
11
+ // @ts-ignore - Optional peer dependency
12
+ import { useNavigation } from '@react-navigation/native';
13
+ // @ts-ignore - Optional peer dependency
14
+ import { useLocalization } from '@umituz/react-native-localization';
15
+
16
+ export interface NotificationsSectionConfig {
17
+ route?: string;
18
+ title?: string;
19
+ description?: string;
20
+ sectionTitle?: string;
21
+ }
22
+
23
+ export interface NotificationsSectionProps {
24
+ config?: NotificationsSectionConfig;
25
+ containerStyle?: StyleProp<ViewStyle>;
26
+ }
27
+
28
+ export const NotificationsSection: React.FC<NotificationsSectionProps> = ({
29
+ config,
30
+ containerStyle,
31
+ }) => {
32
+ const navigation = useNavigation();
33
+ const { t } = useLocalization();
34
+ const tokens = useAppDesignTokens();
35
+ const styles = useMemo(() => createStyles(tokens), [tokens]);
36
+
37
+ const handlePress = useCallback(() => {
38
+ const route = config?.route || 'Notifications';
39
+ navigation.navigate(route as never);
40
+ }, [config?.route, navigation]);
41
+
42
+ const title = config?.title || t('settings.notifications.title');
43
+ const description = config?.description || t('settings.notifications.description');
44
+ const sectionTitle = config?.sectionTitle || t('settings.notifications.sectionTitle');
45
+
46
+ return (
47
+ <View style={[styles.container, containerStyle]}>
48
+ <AtomicText type="bodyLarge" style={styles.sectionTitle}>{sectionTitle}</AtomicText>
49
+
50
+ <TouchableOpacity
51
+ style={styles.itemContainer}
52
+ onPress={handlePress}
53
+ activeOpacity={0.7}
54
+ >
55
+ <View style={styles.iconContainer}>
56
+ <AtomicIcon name="notifications" size="md" color="primary" />
57
+ </View>
58
+ <View style={styles.textContainer}>
59
+ <AtomicText type="bodyLarge">{title}</AtomicText>
60
+ <AtomicText type="bodySmall" style={styles.description}>{description}</AtomicText>
61
+ </View>
62
+ <AtomicIcon name="chevron-forward" size="md" color="secondary" />
63
+ </TouchableOpacity>
64
+ </View>
65
+ );
66
+ };
67
+
68
+ const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
69
+ StyleSheet.create({
70
+ container: { marginBottom: 16, borderRadius: 12, overflow: 'hidden', backgroundColor: tokens.colors.surface },
71
+ sectionTitle: { fontWeight: '600', paddingHorizontal: 16, paddingTop: 16, paddingBottom: 8 },
72
+ itemContainer: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 16, minHeight: 72 },
73
+ iconContainer: {
74
+ width: 48,
75
+ height: 48,
76
+ borderRadius: 12,
77
+ justifyContent: 'center',
78
+ alignItems: 'center',
79
+ marginRight: 16,
80
+ backgroundColor: tokens.colors.surfaceSecondary,
81
+ },
82
+ textContainer: { flex: 1, marginRight: 8 },
83
+ description: { color: tokens.colors.textSecondary, marginTop: 4 },
84
+ });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * RemindersNavRow Styles
3
+ */
4
+
5
+ import { StyleSheet } from 'react-native';
6
+ import type { DesignTokens } from '@umituz/react-native-design-system';
7
+
8
+ export const createStyles = (tokens: DesignTokens) =>
9
+ StyleSheet.create({
10
+ navRow: {
11
+ flexDirection: 'row',
12
+ alignItems: 'center',
13
+ },
14
+ iconContainer: {
15
+ width: 44,
16
+ height: 44,
17
+ borderRadius: 22,
18
+ backgroundColor: tokens.colors.surfaceSecondary,
19
+ justifyContent: 'center',
20
+ alignItems: 'center',
21
+ marginRight: 12,
22
+ },
23
+ textContainer: {
24
+ flex: 1,
25
+ marginRight: 12,
26
+ },
27
+ badge: {
28
+ backgroundColor: tokens.colors.primary,
29
+ paddingHorizontal: 8,
30
+ paddingVertical: 2,
31
+ borderRadius: 10,
32
+ marginRight: 8,
33
+ },
34
+ badgeText: {
35
+ color: tokens.colors.surface,
36
+ fontWeight: '600',
37
+ },
38
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * RemindersNavRow Component
3
+ * Reusable navigation row for reminders section with badge
4
+ */
5
+
6
+ import React from 'react';
7
+ import { View, TouchableOpacity } from 'react-native';
8
+ import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
9
+ import { createStyles } from './RemindersNavRow.styles';
10
+ import { useAppDesignTokens } from '@umituz/react-native-design-system';
11
+
12
+ export interface RemindersNavRowProps {
13
+ title: string;
14
+ description: string;
15
+ count: number;
16
+ onPress: () => void;
17
+ }
18
+
19
+ export const RemindersNavRow: React.FC<RemindersNavRowProps> = ({
20
+ title,
21
+ description,
22
+ count,
23
+ onPress,
24
+ }) => {
25
+ const tokens = useAppDesignTokens();
26
+ const styles = createStyles(tokens);
27
+
28
+ return (
29
+ <TouchableOpacity style={styles.navRow} onPress={onPress} activeOpacity={0.7}>
30
+ <View style={styles.iconContainer}>
31
+ <AtomicIcon name="time" size="md" color="primary" />
32
+ </View>
33
+ <View style={styles.textContainer}>
34
+ <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
35
+ {title}
36
+ </AtomicText>
37
+ <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary }}>
38
+ {description}
39
+ </AtomicText>
40
+ </View>
41
+ {count > 0 && (
42
+ <View style={styles.badge}>
43
+ <AtomicText type="bodySmall" style={styles.badgeText}>
44
+ {count}
45
+ </AtomicText>
46
+ </View>
47
+ )}
48
+ <AtomicIcon name="chevron-forward" size="md" color="secondary" />
49
+ </TouchableOpacity>
50
+ );
51
+ };
@@ -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 '../../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 '../../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 '../../reminders/infrastructure/storage/RemindersStore';
15
+ import { useQuietHoursActions } from '../../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;