@umituz/react-native-notifications 1.5.10 → 1.5.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 +1 -1
- package/src/domains/reminders/infrastructure/hooks/useReminderActions.ts +4 -40
- package/src/domains/reminders/infrastructure/storage/RemindersStore.ts +95 -110
- package/src/domains/reminders/presentation/screens/ReminderListScreen.tsx +0 -5
- package/src/index.ts +18 -0
- package/src/infrastructure/hooks/useNotificationSettings.ts +29 -33
- package/src/infrastructure/services/NotificationManager.ts +1 -26
- package/src/infrastructure/services/NotificationScheduler.ts +1 -1
- package/src/infrastructure/services/types.ts +3 -1
- package/src/infrastructure/utils/idGenerator.ts +14 -0
- package/src/infrastructure/utils/triggerBuilder.ts +45 -0
- package/src/presentation/hooks/useNotificationSettingsUI.ts +3 -2
package/package.json
CHANGED
|
@@ -6,55 +6,19 @@
|
|
|
6
6
|
import { useCallback } from 'react';
|
|
7
7
|
import { useRemindersStore } from '../storage/RemindersStore';
|
|
8
8
|
import { NotificationScheduler } from '../../../../infrastructure/services/NotificationScheduler';
|
|
9
|
-
import
|
|
9
|
+
import { generateReminderId } from '../../../../infrastructure/utils/idGenerator';
|
|
10
|
+
import { buildTrigger } from '../../../../infrastructure/utils/triggerBuilder';
|
|
11
|
+
import type { Reminder, CreateReminderInput, UpdateReminderInput } from '../../../../infrastructure/services/types';
|
|
10
12
|
|
|
11
13
|
const scheduler = new NotificationScheduler();
|
|
12
14
|
|
|
13
|
-
const generateId = (): string => {
|
|
14
|
-
return `reminder_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const buildTrigger = (reminder: Reminder): NotificationTrigger => {
|
|
18
|
-
switch (reminder.frequency) {
|
|
19
|
-
case 'once':
|
|
20
|
-
const date = new Date();
|
|
21
|
-
date.setHours(reminder.hour, reminder.minute, 0, 0);
|
|
22
|
-
if (date <= new Date()) {
|
|
23
|
-
date.setDate(date.getDate() + 1);
|
|
24
|
-
}
|
|
25
|
-
return { type: 'date', date };
|
|
26
|
-
|
|
27
|
-
case 'daily':
|
|
28
|
-
return { type: 'daily', hour: reminder.hour, minute: reminder.minute };
|
|
29
|
-
|
|
30
|
-
case 'weekly':
|
|
31
|
-
return {
|
|
32
|
-
type: 'weekly',
|
|
33
|
-
weekday: reminder.weekday || 2,
|
|
34
|
-
hour: reminder.hour,
|
|
35
|
-
minute: reminder.minute,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
case 'monthly':
|
|
39
|
-
return {
|
|
40
|
-
type: 'monthly',
|
|
41
|
-
day: reminder.dayOfMonth || 1,
|
|
42
|
-
hour: reminder.hour,
|
|
43
|
-
minute: reminder.minute,
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
default:
|
|
47
|
-
return { type: 'daily', hour: reminder.hour, minute: reminder.minute };
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
15
|
export const useReminderActions = () => {
|
|
52
16
|
const { addReminder, updateReminder, deleteReminder, toggleReminder } = useRemindersStore();
|
|
53
17
|
|
|
54
18
|
const createReminder = useCallback(async (input: CreateReminderInput): Promise<Reminder> => {
|
|
55
19
|
const now = new Date().toISOString();
|
|
56
20
|
const reminder: Reminder = {
|
|
57
|
-
id:
|
|
21
|
+
id: generateReminderId(),
|
|
58
22
|
...input,
|
|
59
23
|
enabled: true,
|
|
60
24
|
createdAt: now,
|
|
@@ -4,37 +4,83 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { createStore } from '@umituz/react-native-storage';
|
|
7
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
8
7
|
import type { Reminder, QuietHoursConfig, NotificationPreferences } from '../../../../infrastructure/services/types';
|
|
9
8
|
|
|
10
|
-
const STORAGE_KEYS = {
|
|
11
|
-
REMINDERS: '@notifications:reminders',
|
|
12
|
-
PREFERENCES: '@notifications:preferences',
|
|
13
|
-
QUIET_HOURS: '@notifications:quiet_hours',
|
|
14
|
-
} as const;
|
|
15
|
-
|
|
16
9
|
// ============================================================================
|
|
17
|
-
// STORE
|
|
10
|
+
// REMINDERS STORE
|
|
18
11
|
// ============================================================================
|
|
19
12
|
|
|
20
13
|
interface RemindersState {
|
|
21
14
|
reminders: Reminder[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface RemindersActions {
|
|
18
|
+
addReminder: (reminder: Reminder) => void;
|
|
19
|
+
updateReminder: (id: string, updates: Partial<Reminder>) => void;
|
|
20
|
+
deleteReminder: (id: string) => void;
|
|
21
|
+
toggleReminder: (id: string) => void;
|
|
22
|
+
resetReminders: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DEFAULT_REMINDERS_STATE: RemindersState = {
|
|
26
|
+
reminders: [],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const useRemindersStore = createStore<RemindersState, RemindersActions>({
|
|
30
|
+
name: 'reminders-store',
|
|
31
|
+
initialState: DEFAULT_REMINDERS_STATE,
|
|
32
|
+
persist: true,
|
|
33
|
+
actions: (set, get) => ({
|
|
34
|
+
addReminder: (reminder: Reminder) => {
|
|
35
|
+
const { reminders } = get();
|
|
36
|
+
set({ reminders: [...reminders, reminder] });
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
updateReminder: (id: string, updates: Partial<Reminder>) => {
|
|
40
|
+
const { reminders } = get();
|
|
41
|
+
set({
|
|
42
|
+
reminders: reminders.map(r =>
|
|
43
|
+
r.id === id ? { ...r, ...updates, updatedAt: new Date().toISOString() } : r
|
|
44
|
+
),
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
deleteReminder: (id: string) => {
|
|
49
|
+
const { reminders } = get();
|
|
50
|
+
set({ reminders: reminders.filter(r => r.id !== id) });
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
toggleReminder: (id: string) => {
|
|
54
|
+
const { reminders } = get();
|
|
55
|
+
set({
|
|
56
|
+
reminders: reminders.map(r =>
|
|
57
|
+
r.id === id ? { ...r, enabled: !r.enabled, updatedAt: new Date().toISOString() } : r
|
|
58
|
+
),
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
resetReminders: () => {
|
|
63
|
+
set({ reminders: [] });
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// PREFERENCES STORE
|
|
70
|
+
// ============================================================================
|
|
71
|
+
|
|
72
|
+
interface PreferencesState {
|
|
22
73
|
preferences: NotificationPreferences;
|
|
23
74
|
isLoading: boolean;
|
|
24
75
|
isInitialized: boolean;
|
|
25
76
|
}
|
|
26
77
|
|
|
27
|
-
interface
|
|
78
|
+
interface PreferencesActions {
|
|
28
79
|
initialize: () => Promise<void>;
|
|
29
|
-
loadReminders: () => Promise<void>;
|
|
30
|
-
addReminder: (reminder: Reminder) => Promise<void>;
|
|
31
|
-
updateReminder: (id: string, updates: Partial<Reminder>) => Promise<void>;
|
|
32
|
-
deleteReminder: (id: string) => Promise<void>;
|
|
33
|
-
toggleReminder: (id: string) => Promise<void>;
|
|
34
80
|
loadPreferences: () => Promise<void>;
|
|
35
|
-
updatePreferences: (updates: Partial<NotificationPreferences>) =>
|
|
36
|
-
updateQuietHours: (quietHours: QuietHoursConfig) =>
|
|
37
|
-
reset: () =>
|
|
81
|
+
updatePreferences: (updates: Partial<NotificationPreferences>) => void;
|
|
82
|
+
updateQuietHours: (quietHours: QuietHoursConfig) => void;
|
|
83
|
+
reset: () => void;
|
|
38
84
|
}
|
|
39
85
|
|
|
40
86
|
// ============================================================================
|
|
@@ -54,124 +100,63 @@ const DEFAULT_PREFERENCES: NotificationPreferences = {
|
|
|
54
100
|
},
|
|
55
101
|
};
|
|
56
102
|
|
|
57
|
-
const
|
|
58
|
-
reminders: [],
|
|
103
|
+
const initialPreferencesState: PreferencesState = {
|
|
59
104
|
preferences: DEFAULT_PREFERENCES,
|
|
60
105
|
isLoading: true,
|
|
61
106
|
isInitialized: false,
|
|
62
107
|
};
|
|
63
108
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
export const useRemindersStore = createStore<RemindersState, RemindersActions>({
|
|
69
|
-
name: 'reminders-store',
|
|
70
|
-
initialState: initialRemindersState,
|
|
71
|
-
persist: false, // Manual persistence via AsyncStorage
|
|
109
|
+
export const usePreferencesStore = createStore<PreferencesState, PreferencesActions>({
|
|
110
|
+
name: 'preferences-store',
|
|
111
|
+
initialState: initialPreferencesState,
|
|
112
|
+
persist: true,
|
|
72
113
|
actions: (set, get) => ({
|
|
73
114
|
initialize: async () => {
|
|
74
|
-
|
|
75
|
-
const [remindersData, preferencesData] = await Promise.all([
|
|
76
|
-
AsyncStorage.getItem(STORAGE_KEYS.REMINDERS),
|
|
77
|
-
AsyncStorage.getItem(STORAGE_KEYS.PREFERENCES)
|
|
78
|
-
]);
|
|
79
|
-
|
|
80
|
-
const reminders = remindersData ? JSON.parse(remindersData) : [];
|
|
81
|
-
let preferences = DEFAULT_PREFERENCES;
|
|
82
|
-
|
|
83
|
-
if (preferencesData) {
|
|
84
|
-
const parsed = JSON.parse(preferencesData);
|
|
85
|
-
preferences = { ...DEFAULT_PREFERENCES, ...parsed };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
set({ reminders, preferences, isLoading: false, isInitialized: true });
|
|
89
|
-
} catch {
|
|
90
|
-
set({ reminders: [], preferences: DEFAULT_PREFERENCES, isLoading: false, isInitialized: true });
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
loadReminders: async () => {
|
|
95
|
-
try {
|
|
96
|
-
const data = await AsyncStorage.getItem(STORAGE_KEYS.REMINDERS);
|
|
97
|
-
const reminders = data ? JSON.parse(data) : [];
|
|
98
|
-
set({ reminders });
|
|
99
|
-
} catch {
|
|
100
|
-
set({ reminders: [] });
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
addReminder: async (reminder: Reminder) => {
|
|
105
|
-
const { reminders } = get();
|
|
106
|
-
const updated = [...reminders, reminder];
|
|
107
|
-
set({ reminders: updated });
|
|
108
|
-
await AsyncStorage.setItem(STORAGE_KEYS.REMINDERS, JSON.stringify(updated));
|
|
109
|
-
},
|
|
110
|
-
|
|
111
|
-
updateReminder: async (id: string, updates: Partial<Reminder>) => {
|
|
112
|
-
const { reminders } = get();
|
|
113
|
-
const updated = reminders.map(r =>
|
|
114
|
-
r.id === id ? { ...r, ...updates, updatedAt: new Date().toISOString() } : r
|
|
115
|
-
);
|
|
116
|
-
set({ reminders: updated });
|
|
117
|
-
await AsyncStorage.setItem(STORAGE_KEYS.REMINDERS, JSON.stringify(updated));
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
deleteReminder: async (id: string) => {
|
|
121
|
-
const { reminders } = get();
|
|
122
|
-
const updated = reminders.filter(r => r.id !== id);
|
|
123
|
-
set({ reminders: updated });
|
|
124
|
-
await AsyncStorage.setItem(STORAGE_KEYS.REMINDERS, JSON.stringify(updated));
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
toggleReminder: async (id: string) => {
|
|
128
|
-
const { reminders } = get();
|
|
129
|
-
const updated = reminders.map(r =>
|
|
130
|
-
r.id === id ? { ...r, enabled: !r.enabled, updatedAt: new Date().toISOString() } : r
|
|
131
|
-
);
|
|
132
|
-
set({ reminders: updated });
|
|
133
|
-
await AsyncStorage.setItem(STORAGE_KEYS.REMINDERS, JSON.stringify(updated));
|
|
115
|
+
set({ isLoading: false, isInitialized: true });
|
|
134
116
|
},
|
|
135
117
|
|
|
136
118
|
loadPreferences: async () => {
|
|
137
|
-
|
|
138
|
-
const data = await AsyncStorage.getItem(STORAGE_KEYS.PREFERENCES);
|
|
139
|
-
const preferences = data ? { ...DEFAULT_PREFERENCES, ...JSON.parse(data) } : DEFAULT_PREFERENCES;
|
|
140
|
-
set({ preferences });
|
|
141
|
-
} catch {
|
|
142
|
-
set({ preferences: DEFAULT_PREFERENCES });
|
|
143
|
-
}
|
|
119
|
+
// Data loaded automatically by persist
|
|
144
120
|
},
|
|
145
121
|
|
|
146
|
-
updatePreferences:
|
|
122
|
+
updatePreferences: (updates: Partial<NotificationPreferences>) => {
|
|
147
123
|
const { preferences } = get();
|
|
148
|
-
|
|
149
|
-
set({ preferences: updated });
|
|
150
|
-
await AsyncStorage.setItem(STORAGE_KEYS.PREFERENCES, JSON.stringify(updated));
|
|
124
|
+
set({ preferences: { ...preferences, ...updates } });
|
|
151
125
|
},
|
|
152
126
|
|
|
153
|
-
updateQuietHours:
|
|
127
|
+
updateQuietHours: (quietHours: QuietHoursConfig) => {
|
|
154
128
|
const { preferences } = get();
|
|
155
|
-
|
|
156
|
-
set({ preferences: updated });
|
|
157
|
-
await AsyncStorage.setItem(STORAGE_KEYS.PREFERENCES, JSON.stringify(updated));
|
|
129
|
+
set({ preferences: { ...preferences, quietHours } });
|
|
158
130
|
},
|
|
159
131
|
|
|
160
|
-
reset:
|
|
161
|
-
set({
|
|
162
|
-
await AsyncStorage.multiRemove([STORAGE_KEYS.REMINDERS, STORAGE_KEYS.PREFERENCES]);
|
|
132
|
+
reset: () => {
|
|
133
|
+
set({ preferences: DEFAULT_PREFERENCES });
|
|
163
134
|
},
|
|
164
135
|
}),
|
|
165
136
|
});
|
|
166
137
|
|
|
167
138
|
// ============================================================================
|
|
168
|
-
// SELECTOR HOOKS
|
|
139
|
+
// SELECTOR HOOKS - REMINDERS
|
|
169
140
|
// ============================================================================
|
|
170
141
|
|
|
171
142
|
export const useReminders = () => useRemindersStore(state => state.reminders);
|
|
172
143
|
export const useEnabledReminders = () => useRemindersStore(state => state.reminders.filter(r => r.enabled));
|
|
173
144
|
export const useReminderById = (id: string) => useRemindersStore(state => state.reminders.find(r => r.id === id));
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
145
|
+
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// SELECTOR HOOKS - PREFERENCES
|
|
148
|
+
// ============================================================================
|
|
149
|
+
|
|
150
|
+
export const useNotificationPreferences = () => usePreferencesStore(state => state.preferences);
|
|
151
|
+
export const useQuietHours = () => usePreferencesStore(state => state.preferences.quietHours);
|
|
152
|
+
export const useRemindersLoading = () => usePreferencesStore(state => state.isLoading);
|
|
153
|
+
export const useRemindersInitialized = () => usePreferencesStore(state => state.isInitialized);
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// LEGACY EXPORTS - DEPRECATED
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @deprecated Use specific store hooks instead
|
|
161
|
+
*/
|
|
162
|
+
export const useRemindersStoreLegacy = useRemindersStore;
|
|
@@ -30,13 +30,8 @@ export const ReminderListScreen: React.FC<ReminderListScreenProps> = ({
|
|
|
30
30
|
|
|
31
31
|
const reminders = useReminders();
|
|
32
32
|
const isLoading = useRemindersLoading();
|
|
33
|
-
const { loadReminders } = useRemindersStore();
|
|
34
33
|
const { toggleReminderEnabled, removeReminder } = useReminderActions();
|
|
35
34
|
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
loadReminders();
|
|
38
|
-
}, [loadReminders]);
|
|
39
|
-
|
|
40
35
|
const handleToggle = useCallback(async (id: string) => {
|
|
41
36
|
await toggleReminderEnabled(id);
|
|
42
37
|
}, [toggleReminderEnabled]);
|
package/src/index.ts
CHANGED
|
@@ -58,6 +58,7 @@ export { NotificationManager } from './infrastructure/services/NotificationManag
|
|
|
58
58
|
export { useNotificationsStore, useNotifications } from './infrastructure/storage/NotificationsStore';
|
|
59
59
|
export {
|
|
60
60
|
useRemindersStore,
|
|
61
|
+
usePreferencesStore,
|
|
61
62
|
useReminders,
|
|
62
63
|
useEnabledReminders,
|
|
63
64
|
useReminderById,
|
|
@@ -117,5 +118,22 @@ export type { FormButtonProps } from './domains/reminders/presentation/component
|
|
|
117
118
|
export { QuietHoursCard } from './domains/quietHours/presentation/components/QuietHoursCard';
|
|
118
119
|
export type { QuietHoursCardProps } from './domains/quietHours/presentation/components/QuietHoursCard';
|
|
119
120
|
|
|
121
|
+
export { RemindersNavRow } from './presentation/components/RemindersNavRow';
|
|
122
|
+
export type { RemindersNavRowProps } from './presentation/components/RemindersNavRow';
|
|
123
|
+
|
|
120
124
|
export { SettingRow } from './presentation/components/SettingRow';
|
|
121
125
|
export type { SettingRowProps } from './presentation/components/SettingRow';
|
|
126
|
+
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// UTILS
|
|
129
|
+
// ============================================================================
|
|
130
|
+
|
|
131
|
+
export { generateId, generateReminderId } from './infrastructure/utils/idGenerator';
|
|
132
|
+
export { buildTrigger } from './infrastructure/utils/triggerBuilder';
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// HOOKS - UTILS
|
|
136
|
+
// ============================================================================
|
|
137
|
+
|
|
138
|
+
export { useTimePicker } from './presentation/hooks/useTimePicker';
|
|
139
|
+
export type { PickerMode, UseTimePickerParams, TimePickerHandlers } from './presentation/hooks/useTimePicker';
|
|
@@ -1,45 +1,41 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
2
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
3
|
-
|
|
4
|
-
const STORAGE_KEY = 'notifications_enabled';
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* Simple notification settings hook
|
|
8
|
-
*
|
|
3
|
+
* @deprecated Use usePreferencesStore and useNotificationPreferences instead
|
|
9
4
|
*/
|
|
10
|
-
export const useNotificationSettings = () => {
|
|
11
|
-
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
|
|
12
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
13
5
|
|
|
14
|
-
|
|
15
|
-
const loadSettings = async () => {
|
|
16
|
-
try {
|
|
17
|
-
const value = await AsyncStorage.getItem(STORAGE_KEY);
|
|
18
|
-
if (value !== null) {
|
|
19
|
-
setNotificationsEnabled(value === 'true');
|
|
20
|
-
}
|
|
21
|
-
} catch (error) {
|
|
22
|
-
// Silent failure - use default value
|
|
23
|
-
} finally {
|
|
24
|
-
setIsLoading(false);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
6
|
+
import { createStore } from '@umituz/react-native-storage';
|
|
27
7
|
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
interface NotificationSettingsState {
|
|
9
|
+
notificationsEnabled: boolean;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
}
|
|
30
12
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
13
|
+
interface NotificationSettingsActions {
|
|
14
|
+
setNotificationsEnabled: (value: boolean) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const useNotificationSettingsStore = createStore<NotificationSettingsState, NotificationSettingsActions>({
|
|
18
|
+
name: 'notification-settings-store',
|
|
19
|
+
initialState: {
|
|
20
|
+
notificationsEnabled: true,
|
|
21
|
+
isLoading: true,
|
|
22
|
+
},
|
|
23
|
+
persist: true,
|
|
24
|
+
actions: (set) => ({
|
|
25
|
+
setNotificationsEnabled: (value: boolean) => set({ notificationsEnabled: value }),
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @deprecated Use usePreferencesStore and useNotificationPreferences from RemindersStore instead
|
|
31
|
+
*/
|
|
32
|
+
export const useNotificationSettings = () => {
|
|
33
|
+
const store = useNotificationSettingsStore();
|
|
34
|
+
const { notificationsEnabled, isLoading, setNotificationsEnabled } = store;
|
|
39
35
|
|
|
40
36
|
return {
|
|
41
37
|
notificationsEnabled,
|
|
42
|
-
setNotificationsEnabled
|
|
38
|
+
setNotificationsEnabled,
|
|
43
39
|
isLoading,
|
|
44
40
|
};
|
|
45
41
|
};
|
|
@@ -12,32 +12,7 @@ import { NotificationPermissions } from './NotificationPermissions';
|
|
|
12
12
|
import { NotificationScheduler } from './NotificationScheduler';
|
|
13
13
|
import { NotificationBadgeManager } from './NotificationBadgeManager';
|
|
14
14
|
import { devLog, devError } from '../utils/dev';
|
|
15
|
-
|
|
16
|
-
export type NotificationTrigger =
|
|
17
|
-
| { type: 'date'; date: Date }
|
|
18
|
-
| { type: 'daily'; hour: number; minute: number }
|
|
19
|
-
| { type: 'weekly'; weekday: number; hour: number; minute: number }
|
|
20
|
-
| { type: 'monthly'; day: number; hour: number; minute: number };
|
|
21
|
-
|
|
22
|
-
export interface ScheduleNotificationOptions {
|
|
23
|
-
title: string;
|
|
24
|
-
body: string;
|
|
25
|
-
data?: Record<string, any>;
|
|
26
|
-
trigger: NotificationTrigger;
|
|
27
|
-
sound?: boolean | string;
|
|
28
|
-
badge?: number;
|
|
29
|
-
categoryIdentifier?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface ScheduledNotification {
|
|
33
|
-
identifier: string;
|
|
34
|
-
content: {
|
|
35
|
-
title: string;
|
|
36
|
-
body: string;
|
|
37
|
-
data: Record<string, any>;
|
|
38
|
-
};
|
|
39
|
-
trigger: any;
|
|
40
|
-
}
|
|
15
|
+
import type { NotificationTrigger, ScheduleNotificationOptions, ScheduledNotification } from './types';
|
|
41
16
|
|
|
42
17
|
export class NotificationManager {
|
|
43
18
|
private permissions: NotificationPermissions;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Notifications from 'expo-notifications';
|
|
2
|
-
import type { NotificationTrigger, ScheduleNotificationOptions, ScheduledNotification } from './
|
|
2
|
+
import type { NotificationTrigger, ScheduleNotificationOptions, ScheduledNotification } from './types';
|
|
3
3
|
|
|
4
4
|
export class NotificationScheduler {
|
|
5
5
|
async scheduleNotification(options: ScheduleNotificationOptions): Promise<string> {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Uses expo-notifications for local device notifications
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import * as Notifications from 'expo-notifications';
|
|
7
|
+
|
|
6
8
|
// ============================================================================
|
|
7
9
|
// NOTIFICATION CHANNEL TYPES
|
|
8
10
|
// ============================================================================
|
|
@@ -44,7 +46,7 @@ export interface ScheduledNotification {
|
|
|
44
46
|
body: string;
|
|
45
47
|
data: Record<string, unknown>;
|
|
46
48
|
};
|
|
47
|
-
trigger:
|
|
49
|
+
trigger: Notifications.NotificationTrigger;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
// ============================================================================
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Generator Utility
|
|
3
|
+
* Generates unique IDs for entities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const generateId = (prefix: string = 'id'): string => {
|
|
7
|
+
const timestamp = Date.now();
|
|
8
|
+
const random = Math.random().toString(36).substring(2, 11);
|
|
9
|
+
return `${prefix}_${timestamp}_${random}`;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const generateReminderId = (): string => {
|
|
13
|
+
return generateId('reminder');
|
|
14
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger Builder Utility
|
|
3
|
+
* Builds notification triggers from reminder data
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { NotificationTrigger, Reminder } from '../services/types';
|
|
7
|
+
|
|
8
|
+
export const buildTrigger = (reminder: Reminder): NotificationTrigger => {
|
|
9
|
+
const { frequency, hour, minute, weekday, dayOfMonth } = reminder;
|
|
10
|
+
|
|
11
|
+
switch (frequency) {
|
|
12
|
+
case 'once': {
|
|
13
|
+
const date = new Date();
|
|
14
|
+
date.setHours(hour, minute, 0, 0);
|
|
15
|
+
|
|
16
|
+
if (date <= new Date()) {
|
|
17
|
+
date.setDate(date.getDate() + 1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return { type: 'date', date };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
case 'daily':
|
|
24
|
+
return { type: 'daily', hour, minute };
|
|
25
|
+
|
|
26
|
+
case 'weekly':
|
|
27
|
+
return {
|
|
28
|
+
type: 'weekly',
|
|
29
|
+
weekday: weekday || 1,
|
|
30
|
+
hour,
|
|
31
|
+
minute,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
case 'monthly':
|
|
35
|
+
return {
|
|
36
|
+
type: 'monthly',
|
|
37
|
+
day: dayOfMonth || 1,
|
|
38
|
+
hour,
|
|
39
|
+
minute,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
default:
|
|
43
|
+
return { type: 'daily', hour, minute };
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { useEffect, useCallback } from 'react';
|
|
7
|
-
import { useNotificationPreferences, useQuietHours,
|
|
7
|
+
import { useNotificationPreferences, useQuietHours, usePreferencesStore, useRemindersLoading } from '../../domains/reminders/infrastructure/storage/RemindersStore';
|
|
8
8
|
import { notificationService } from '../../infrastructure/services/NotificationService';
|
|
9
9
|
|
|
10
10
|
export const useNotificationSettingsUI = () => {
|
|
11
11
|
const preferences = useNotificationPreferences();
|
|
12
12
|
const quietHours = useQuietHours();
|
|
13
|
-
const { initialize, updatePreferences, updateQuietHours
|
|
13
|
+
const { initialize, updatePreferences, updateQuietHours } = usePreferencesStore();
|
|
14
|
+
const isLoading = useRemindersLoading();
|
|
14
15
|
|
|
15
16
|
useEffect(() => {
|
|
16
17
|
initialize();
|