@umituz/react-native-notifications 1.3.10 → 1.3.11

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-notifications",
3
- "version": "1.3.10",
3
+ "version": "1.3.11",
4
4
  "description": "Offline-first local notifications system for React Native apps using expo-notifications",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
package/src/index.ts CHANGED
@@ -74,6 +74,7 @@ export {
74
74
  export { useNotificationSettings } from './infrastructure/hooks/useNotificationSettings';
75
75
  export { useReminderActions } from './infrastructure/hooks/useReminderActions';
76
76
  export { useQuietHoursActions } from './infrastructure/hooks/useQuietHoursActions';
77
+ export { useNotificationSettingsUI } from './presentation/hooks/useNotificationSettingsUI';
77
78
 
78
79
  // ============================================================================
79
80
  // SCREENS
@@ -115,3 +116,6 @@ export type { FormButtonProps } from './presentation/components/FormButton';
115
116
 
116
117
  export { QuietHoursCard } from './presentation/components/QuietHoursCard';
117
118
  export type { QuietHoursCardProps } from './presentation/components/QuietHoursCard';
119
+
120
+ export { SettingRow } from './presentation/components/SettingRow';
121
+ export type { SettingRowProps } from './presentation/components/SettingRow';
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Setting Row Component
3
+ * Reusable toggle row for settings
4
+ */
5
+
6
+ import React, { useMemo } 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-theme';
10
+
11
+ export interface SettingRowProps {
12
+ iconName: string;
13
+ title: string;
14
+ description: string;
15
+ value: boolean;
16
+ onToggle: (value: boolean) => void;
17
+ }
18
+
19
+ export const SettingRow: React.FC<SettingRowProps> = ({
20
+ iconName,
21
+ title,
22
+ description,
23
+ value,
24
+ onToggle
25
+ }) => {
26
+ const tokens = useAppDesignTokens();
27
+ const styles = useMemo(() => createStyles(tokens), [tokens]);
28
+
29
+ return (
30
+ <View style={styles.container}>
31
+ <View style={styles.iconContainer}>
32
+ <AtomicIcon name={iconName} size="md" color="primary" />
33
+ </View>
34
+ <View style={styles.textContainer}>
35
+ <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
36
+ {title}
37
+ </AtomicText>
38
+ <AtomicText type="bodySmall" style={styles.description}>
39
+ {description}
40
+ </AtomicText>
41
+ </View>
42
+ <Switch
43
+ value={value}
44
+ onValueChange={onToggle}
45
+ trackColor={{
46
+ false: tokens.colors.surfaceSecondary,
47
+ true: tokens.colors.primary
48
+ }}
49
+ thumbColor={tokens.colors.surface}
50
+ ios_backgroundColor={tokens.colors.surfaceSecondary}
51
+ />
52
+ </View>
53
+ );
54
+ };
55
+
56
+ const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
57
+ StyleSheet.create({
58
+ container: {
59
+ flexDirection: 'row',
60
+ alignItems: 'center',
61
+ },
62
+ iconContainer: {
63
+ width: 44,
64
+ height: 44,
65
+ borderRadius: 22,
66
+ backgroundColor: tokens.colors.surfaceSecondary,
67
+ justifyContent: 'center',
68
+ alignItems: 'center',
69
+ marginRight: 12,
70
+ },
71
+ textContainer: {
72
+ flex: 1,
73
+ marginRight: 12,
74
+ },
75
+ description: {
76
+ color: tokens.colors.textSecondary,
77
+ marginTop: 2,
78
+ },
79
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * useNotificationSettingsUI Hook
3
+ * Handles all business logic for notification settings screen
4
+ */
5
+
6
+ import { useEffect, useCallback } from 'react';
7
+ import { useRemindersStore, useNotificationPreferences, useQuietHours } from '../../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, isLoading } = useRemindersStore();
14
+
15
+ useEffect(() => {
16
+ initialize();
17
+ }, [initialize]);
18
+
19
+ const handleMasterToggle = useCallback(async (value: boolean) => {
20
+ if (value) {
21
+ const hasPermission = await notificationService.hasPermissions();
22
+ if (!hasPermission) {
23
+ const granted = await notificationService.requestPermissions();
24
+ if (!granted) return;
25
+ }
26
+ }
27
+ await updatePreferences({ enabled: value });
28
+ }, [updatePreferences]);
29
+
30
+ const handleSoundToggle = useCallback(async (value: boolean) => {
31
+ await updatePreferences({ sound: value });
32
+ }, [updatePreferences]);
33
+
34
+ const handleVibrationToggle = useCallback(async (value: boolean) => {
35
+ await updatePreferences({ vibration: value });
36
+ }, [updatePreferences]);
37
+
38
+ const handleQuietHoursToggle = useCallback(async (value: boolean) => {
39
+ await updateQuietHours({ ...quietHours, enabled: value });
40
+ }, [quietHours, updateQuietHours]);
41
+
42
+ return {
43
+ preferences,
44
+ quietHours,
45
+ isLoading,
46
+ handleMasterToggle,
47
+ handleSoundToggle,
48
+ handleVibrationToggle,
49
+ handleQuietHoursToggle,
50
+ };
51
+ };
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * NotificationSettingsScreen
3
- * Professional notification settings with all options
3
+ * Clean presentation-only screen for notification settings
4
4
  */
5
5
 
6
- import React, { useEffect, useMemo, useCallback } from 'react';
7
- import { View, StyleSheet, ActivityIndicator, ScrollView, TouchableOpacity } from 'react-native';
6
+ import React, { useMemo } from 'react';
7
+ import { View, StyleSheet, ActivityIndicator, TouchableOpacity } from 'react-native';
8
8
  import { AtomicText, AtomicIcon, AtomicCard, ScreenLayout } from '@umituz/react-native-design-system';
9
- import { Switch } from 'react-native';
10
9
  import { useAppDesignTokens } from '@umituz/react-native-design-system-theme';
11
10
  import { QuietHoursCard } from '../components/QuietHoursCard';
12
- import { useRemindersStore, useNotificationPreferences, useQuietHours, useReminders } from '../../infrastructure/storage/RemindersStore';
13
- import { notificationService } from '../../infrastructure/services/NotificationService';
11
+ import { SettingRow } from '../components/SettingRow';
12
+ import { useNotificationSettingsUI } from '../hooks/useNotificationSettingsUI';
13
+ import { useReminders } from '../../infrastructure/storage/RemindersStore';
14
14
  import type { NotificationSettingsTranslations, QuietHoursTranslations } from '../../infrastructure/services/types';
15
15
 
16
16
  export interface NotificationSettingsScreenProps {
@@ -30,38 +30,17 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
30
30
  }) => {
31
31
  const tokens = useAppDesignTokens();
32
32
  const styles = useMemo(() => createStyles(tokens), [tokens]);
33
-
34
- const preferences = useNotificationPreferences();
35
- const quietHours = useQuietHours();
36
33
  const reminders = useReminders();
37
- const { initialize, updatePreferences, updateQuietHours, isLoading } = useRemindersStore();
38
-
39
- useEffect(() => {
40
- initialize();
41
- }, [initialize]);
42
-
43
- const handleMasterToggle = useCallback(async (value: boolean) => {
44
- if (value) {
45
- const hasPermission = await notificationService.hasPermissions();
46
- if (!hasPermission) {
47
- const granted = await notificationService.requestPermissions();
48
- if (!granted) return;
49
- }
50
- }
51
- await updatePreferences({ enabled: value });
52
- }, [updatePreferences]);
53
-
54
- const handleSoundToggle = useCallback(async (value: boolean) => {
55
- await updatePreferences({ sound: value });
56
- }, [updatePreferences]);
57
-
58
- const handleVibrationToggle = useCallback(async (value: boolean) => {
59
- await updatePreferences({ vibration: value });
60
- }, [updatePreferences]);
61
34
 
62
- const handleQuietHoursToggle = useCallback(async (value: boolean) => {
63
- await updateQuietHours({ ...quietHours, enabled: value });
64
- }, [quietHours, updateQuietHours]);
35
+ const {
36
+ preferences,
37
+ quietHours,
38
+ isLoading,
39
+ handleMasterToggle,
40
+ handleSoundToggle,
41
+ handleVibrationToggle,
42
+ handleQuietHoursToggle,
43
+ } = useNotificationSettingsUI();
65
44
 
66
45
  if (isLoading) {
67
46
  return (
@@ -75,7 +54,7 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
75
54
 
76
55
  return (
77
56
  <ScreenLayout hideScrollIndicator>
78
- <ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
57
+ <View style={styles.container}>
79
58
  <AtomicCard style={styles.card}>
80
59
  <SettingRow
81
60
  iconName="notifications"
@@ -83,7 +62,6 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
83
62
  description={translations.masterToggleDescription}
84
63
  value={preferences.enabled}
85
64
  onToggle={handleMasterToggle}
86
- tokens={tokens}
87
65
  />
88
66
  </AtomicCard>
89
67
 
@@ -96,7 +74,6 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
96
74
  description={translations.soundDescription}
97
75
  value={preferences.sound}
98
76
  onToggle={handleSoundToggle}
99
- tokens={tokens}
100
77
  />
101
78
  <View style={styles.divider} />
102
79
  <SettingRow
@@ -105,7 +82,6 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
105
82
  description={translations.vibrationDescription}
106
83
  value={preferences.vibration}
107
84
  onToggle={handleVibrationToggle}
108
- tokens={tokens}
109
85
  />
110
86
  </AtomicCard>
111
87
 
@@ -115,12 +91,18 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
115
91
  <AtomicIcon name="time" size="md" color="primary" />
116
92
  </View>
117
93
  <View style={styles.textContainer}>
118
- <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>{translations.remindersTitle}</AtomicText>
119
- <AtomicText type="bodySmall" style={[styles.description, { color: tokens.colors.textSecondary }]}>{translations.remindersDescription}</AtomicText>
94
+ <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
95
+ {translations.remindersTitle}
96
+ </AtomicText>
97
+ <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary }}>
98
+ {translations.remindersDescription}
99
+ </AtomicText>
120
100
  </View>
121
101
  {reminders.length > 0 && (
122
102
  <View style={styles.badge}>
123
- <AtomicText type="bodySmall" style={styles.badgeText}>{reminders.length}</AtomicText>
103
+ <AtomicText type="bodySmall" style={styles.badgeText}>
104
+ {reminders.length}
105
+ </AtomicText>
124
106
  </View>
125
107
  )}
126
108
  <AtomicIcon name="chevron-forward" size="md" color="secondary" />
@@ -136,49 +118,36 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
136
118
  />
137
119
  </>
138
120
  )}
139
- </ScrollView>
140
- </ScreenLayout>
141
- );
142
- };
143
-
144
- interface SettingRowProps {
145
- iconName: string;
146
- title: string;
147
- description: string;
148
- value: boolean;
149
- onToggle: (value: boolean) => void;
150
- tokens: ReturnType<typeof useAppDesignTokens>;
151
- }
152
-
153
- const SettingRow: React.FC<SettingRowProps> = ({ iconName, title, description, value, onToggle, tokens }) => {
154
- const styles = useMemo(() => createRowStyles(tokens), [tokens]);
155
- return (
156
- <View style={styles.container}>
157
- <View style={styles.iconContainer}>
158
- <AtomicIcon name={iconName} size="md" color="primary" />
159
121
  </View>
160
- <View style={styles.textContainer}>
161
- <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>{title}</AtomicText>
162
- <AtomicText type="bodySmall" style={styles.description}>{description}</AtomicText>
163
- </View>
164
- <Switch
165
- value={value}
166
- onValueChange={onToggle}
167
- trackColor={{ false: tokens.colors.surfaceSecondary, true: tokens.colors.primary }}
168
- thumbColor={tokens.colors.surface}
169
- ios_backgroundColor={tokens.colors.surfaceSecondary}
170
- />
171
- </View>
122
+ </ScreenLayout>
172
123
  );
173
124
  };
174
125
 
175
126
  const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
176
127
  StyleSheet.create({
177
- loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' },
178
- scrollView: { flex: 1, padding: 16 },
179
- card: { marginBottom: 16, padding: 16, backgroundColor: tokens.colors.surface },
180
- divider: { height: 1, backgroundColor: tokens.colors.surfaceSecondary, marginVertical: 12 },
181
- navRow: { flexDirection: 'row', alignItems: 'center' },
128
+ container: {
129
+ flex: 1,
130
+ padding: 16,
131
+ },
132
+ loadingContainer: {
133
+ flex: 1,
134
+ justifyContent: 'center',
135
+ alignItems: 'center',
136
+ },
137
+ card: {
138
+ marginBottom: 16,
139
+ padding: 16,
140
+ backgroundColor: tokens.colors.surface,
141
+ },
142
+ divider: {
143
+ height: 1,
144
+ backgroundColor: tokens.colors.surfaceSecondary,
145
+ marginVertical: 12,
146
+ },
147
+ navRow: {
148
+ flexDirection: 'row',
149
+ alignItems: 'center',
150
+ },
182
151
  iconContainer: {
183
152
  width: 44,
184
153
  height: 44,
@@ -188,8 +157,10 @@ const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
188
157
  alignItems: 'center',
189
158
  marginRight: 12,
190
159
  },
191
- textContainer: { flex: 1, marginRight: 12 },
192
- description: { color: tokens.colors.textSecondary, marginTop: 2 },
160
+ textContainer: {
161
+ flex: 1,
162
+ marginRight: 12,
163
+ },
193
164
  badge: {
194
165
  backgroundColor: tokens.colors.primary,
195
166
  paddingHorizontal: 8,
@@ -197,21 +168,8 @@ const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
197
168
  borderRadius: 10,
198
169
  marginRight: 8,
199
170
  },
200
- badgeText: { color: tokens.colors.surface, fontWeight: '600' },
201
- });
202
-
203
- const createRowStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
204
- StyleSheet.create({
205
- container: { flexDirection: 'row', alignItems: 'center' },
206
- iconContainer: {
207
- width: 44,
208
- height: 44,
209
- borderRadius: 22,
210
- backgroundColor: tokens.colors.surfaceSecondary,
211
- justifyContent: 'center',
212
- alignItems: 'center',
213
- marginRight: 12,
171
+ badgeText: {
172
+ color: tokens.colors.surface,
173
+ fontWeight: '600',
214
174
  },
215
- textContainer: { flex: 1, marginRight: 12 },
216
- description: { color: tokens.colors.textSecondary, marginTop: 2 },
217
175
  });