@umituz/react-native-notifications 1.5.8 → 1.5.9

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.5.8",
3
+ "version": "1.5.9",
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",
@@ -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,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
+ });
@@ -3,21 +3,22 @@
3
3
  * Clean presentation-only screen for notification settings
4
4
  */
5
5
 
6
- import React, { useMemo, useState } from 'react';
7
- import { View, StyleSheet, TouchableOpacity } from 'react-native';
8
- import { AtomicText, AtomicIcon, AtomicCard, ScreenLayout, AtomicSpinner } from '@umituz/react-native-design-system';
9
- import { useAppDesignTokens } from '@umituz/react-native-design-system';
6
+ import React from 'react';
7
+ import { View } from 'react-native';
8
+ import { AtomicCard, ScreenLayout, AtomicSpinner } from '@umituz/react-native-design-system';
10
9
  import { QuietHoursCard } from '../../domains/quietHours/presentation/components/QuietHoursCard';
11
10
  import { SettingRow } from '../components/SettingRow';
11
+ import { RemindersNavRow } from '../components/RemindersNavRow';
12
12
  import { useNotificationSettingsUI } from '../hooks/useNotificationSettingsUI';
13
+ import { useTimePicker } from '../hooks/useTimePicker';
13
14
  import { useReminders } from '../../domains/reminders/infrastructure/storage/RemindersStore';
14
15
  import { useQuietHoursActions } from '../../domains/quietHours/infrastructure/hooks/useQuietHoursActions';
15
16
  import type { NotificationSettingsTranslations, QuietHoursTranslations } from '../../infrastructure/services/types';
17
+ import { createStyles } from './NotificationSettingsScreen.styles';
18
+ import { useAppDesignTokens } from '@umituz/react-native-design-system';
16
19
  // @ts-ignore - Optional peer dependency
17
20
  import DateTimePicker from '@react-native-community/datetimepicker';
18
21
 
19
- type PickerMode = 'start' | 'end' | null;
20
-
21
22
  export interface NotificationSettingsScreenProps {
22
23
  translations: NotificationSettingsTranslations;
23
24
  quietHoursTranslations: QuietHoursTranslations;
@@ -30,10 +31,9 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
30
31
  onHapticFeedback,
31
32
  }) => {
32
33
  const tokens = useAppDesignTokens();
33
- const styles = useMemo(() => createStyles(tokens), [tokens]);
34
+ const styles = createStyles(tokens);
34
35
  const reminders = useReminders();
35
36
  const { setStartTime, setEndTime } = useQuietHoursActions();
36
- const [pickerMode, setPickerMode] = useState<PickerMode>(null);
37
37
 
38
38
  const {
39
39
  preferences,
@@ -45,42 +45,16 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
45
45
  handleQuietHoursToggle,
46
46
  } = useNotificationSettingsUI();
47
47
 
48
+ const timePicker = useTimePicker({
49
+ quietHours,
50
+ onStartTimeChange: setStartTime,
51
+ onEndTimeChange: setEndTime,
52
+ });
53
+
48
54
  const handleRemindersPress = () => {
49
55
  // Navigate to reminders screen when implemented
50
56
  };
51
57
 
52
- const handleStartTimePress = () => {
53
- setPickerMode('start');
54
- };
55
-
56
- const handleEndTimePress = () => {
57
- setPickerMode('end');
58
- };
59
-
60
- const handleTimeChange = (event: any, selectedDate?: Date) => {
61
- if (event.type === 'set' && selectedDate) {
62
- const hours = selectedDate.getHours();
63
- const minutes = selectedDate.getMinutes();
64
-
65
- if (pickerMode === 'start') {
66
- setStartTime(hours, minutes);
67
- } else if (pickerMode === 'end') {
68
- setEndTime(hours, minutes);
69
- }
70
- }
71
- setPickerMode(null);
72
- };
73
-
74
- const getPickerDate = () => {
75
- const date = new Date();
76
- if (pickerMode === 'start') {
77
- date.setHours(quietHours.startHour, quietHours.startMinute);
78
- } else if (pickerMode === 'end') {
79
- date.setHours(quietHours.endHour, quietHours.endMinute);
80
- }
81
- return date;
82
- };
83
-
84
58
  if (isLoading) {
85
59
  return (
86
60
  <ScreenLayout>
@@ -126,98 +100,32 @@ export const NotificationSettingsScreen: React.FC<NotificationSettingsScreenProp
126
100
  </AtomicCard>
127
101
 
128
102
  <AtomicCard style={styles.card}>
129
- <TouchableOpacity style={styles.navRow} onPress={handleRemindersPress} activeOpacity={0.7}>
130
- <View style={styles.iconContainer}>
131
- <AtomicIcon name="time" size="md" color="primary" />
132
- </View>
133
- <View style={styles.textContainer}>
134
- <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
135
- {translations.remindersTitle}
136
- </AtomicText>
137
- <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary }}>
138
- {translations.remindersDescription}
139
- </AtomicText>
140
- </View>
141
- {reminders.length > 0 && (
142
- <View style={styles.badge}>
143
- <AtomicText type="bodySmall" style={styles.badgeText}>
144
- {reminders.length}
145
- </AtomicText>
146
- </View>
147
- )}
148
- <AtomicIcon name="chevron-forward" size="md" color="secondary" />
149
- </TouchableOpacity>
103
+ <RemindersNavRow
104
+ title={translations.remindersTitle}
105
+ description={translations.remindersDescription}
106
+ count={reminders.length}
107
+ onPress={handleRemindersPress}
108
+ />
150
109
  </AtomicCard>
151
110
 
152
111
  <QuietHoursCard
153
112
  config={quietHours}
154
113
  translations={quietHoursTranslations}
155
114
  onToggle={handleQuietHoursToggle}
156
- onStartTimePress={handleStartTimePress}
157
- onEndTimePress={handleEndTimePress}
115
+ onStartTimePress={timePicker.handleStartTimePress}
116
+ onEndTimePress={timePicker.handleEndTimePress}
158
117
  />
159
118
  </>
160
119
  )}
161
120
  </View>
162
- {pickerMode && (
121
+ {timePicker.pickerMode && (
163
122
  <DateTimePicker
164
- value={getPickerDate()}
123
+ value={timePicker.getPickerDate()}
165
124
  mode="time"
166
125
  is24Hour={true}
167
- onChange={handleTimeChange}
126
+ onChange={timePicker.handleTimeChange}
168
127
  />
169
128
  )}
170
129
  </ScreenLayout>
171
130
  );
172
131
  };
173
-
174
- const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
175
- StyleSheet.create({
176
- container: {
177
- flex: 1,
178
- padding: 16,
179
- },
180
- loadingContainer: {
181
- flex: 1,
182
- justifyContent: 'center',
183
- alignItems: 'center',
184
- },
185
- card: {
186
- marginBottom: 16,
187
- padding: 16,
188
- backgroundColor: tokens.colors.surface,
189
- },
190
- divider: {
191
- height: 1,
192
- backgroundColor: tokens.colors.surfaceSecondary,
193
- marginVertical: 12,
194
- },
195
- navRow: {
196
- flexDirection: 'row',
197
- alignItems: 'center',
198
- },
199
- iconContainer: {
200
- width: 44,
201
- height: 44,
202
- borderRadius: 22,
203
- backgroundColor: tokens.colors.surfaceSecondary,
204
- justifyContent: 'center',
205
- alignItems: 'center',
206
- marginRight: 12,
207
- },
208
- textContainer: {
209
- flex: 1,
210
- marginRight: 12,
211
- },
212
- badge: {
213
- backgroundColor: tokens.colors.primary,
214
- paddingHorizontal: 8,
215
- paddingVertical: 2,
216
- borderRadius: 10,
217
- marginRight: 8,
218
- },
219
- badgeText: {
220
- color: tokens.colors.surface,
221
- fontWeight: '600',
222
- },
223
- });