@umituz/react-native-notifications 1.0.6 → 1.1.0

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 (67) hide show
  1. package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts +4 -13
  2. package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts.map +1 -1
  3. package/lib/infrastructure/hooks/actions/useNotificationActions.js +4 -70
  4. package/lib/infrastructure/hooks/actions/useNotificationActions.js.map +1 -1
  5. package/lib/infrastructure/hooks/actions/useNotificationManagementActions.d.ts +8 -0
  6. package/lib/infrastructure/hooks/actions/useNotificationManagementActions.d.ts.map +1 -0
  7. package/lib/infrastructure/hooks/actions/useNotificationManagementActions.js +78 -0
  8. package/lib/infrastructure/hooks/actions/useNotificationManagementActions.js.map +1 -0
  9. package/lib/infrastructure/hooks/state/useNotificationsState.d.ts +8 -8
  10. package/lib/infrastructure/hooks/useNotificationSettings.d.ts +2 -2
  11. package/lib/infrastructure/hooks/useNotifications.d.ts +21 -1
  12. package/lib/infrastructure/hooks/useNotifications.d.ts.map +1 -1
  13. package/lib/infrastructure/hooks/useNotifications.js +30 -9
  14. package/lib/infrastructure/hooks/useNotifications.js.map +1 -1
  15. package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts +5 -10
  16. package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts.map +1 -1
  17. package/lib/infrastructure/hooks/utils/useNotificationRefresh.js +32 -15
  18. package/lib/infrastructure/hooks/utils/useNotificationRefresh.js.map +1 -1
  19. package/lib/infrastructure/services/NotificationBadgeManager.d.ts +5 -0
  20. package/lib/infrastructure/services/NotificationBadgeManager.d.ts.map +1 -0
  21. package/lib/infrastructure/services/NotificationBadgeManager.js +29 -0
  22. package/lib/infrastructure/services/NotificationBadgeManager.js.map +1 -0
  23. package/lib/infrastructure/services/NotificationManager.d.ts +5 -84
  24. package/lib/infrastructure/services/NotificationManager.d.ts.map +1 -1
  25. package/lib/infrastructure/services/NotificationManager.js +36 -203
  26. package/lib/infrastructure/services/NotificationManager.js.map +1 -1
  27. package/lib/infrastructure/services/NotificationPermissions.d.ts +6 -0
  28. package/lib/infrastructure/services/NotificationPermissions.d.ts.map +1 -0
  29. package/lib/infrastructure/services/NotificationPermissions.js +75 -0
  30. package/lib/infrastructure/services/NotificationPermissions.js.map +1 -0
  31. package/lib/infrastructure/services/NotificationScheduler.d.ts +8 -0
  32. package/lib/infrastructure/services/NotificationScheduler.d.ts.map +1 -0
  33. package/lib/infrastructure/services/NotificationScheduler.js +72 -0
  34. package/lib/infrastructure/services/NotificationScheduler.js.map +1 -0
  35. package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts +2 -8
  36. package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts.map +1 -1
  37. package/lib/infrastructure/services/delivery/NotificationDelivery.js +27 -13
  38. package/lib/infrastructure/services/delivery/NotificationDelivery.js.map +1 -1
  39. package/lib/infrastructure/storage/NotificationsStore.d.ts +8 -1
  40. package/lib/infrastructure/storage/NotificationsStore.d.ts.map +1 -1
  41. package/lib/infrastructure/storage/NotificationsStore.js +2 -1
  42. package/lib/infrastructure/storage/NotificationsStore.js.map +1 -1
  43. package/lib/infrastructure/utils/dev.d.ts +5 -0
  44. package/lib/infrastructure/utils/dev.d.ts.map +1 -0
  45. package/lib/infrastructure/utils/dev.js +24 -0
  46. package/lib/infrastructure/utils/dev.js.map +1 -0
  47. package/lib/presentation/screens/NotificationsScreen.d.ts +14 -4
  48. package/lib/presentation/screens/NotificationsScreen.d.ts.map +1 -1
  49. package/lib/presentation/screens/NotificationsScreen.js +12 -15
  50. package/lib/presentation/screens/NotificationsScreen.js.map +1 -1
  51. package/package.json +1 -1
  52. package/src/__tests__/NotificationManager.test.ts +215 -0
  53. package/src/__tests__/useNotificationActions.test.ts +189 -0
  54. package/src/__tests__/useNotificationRefresh.test.ts +213 -0
  55. package/src/infrastructure/hooks/actions/useNotificationActions.ts +8 -110
  56. package/src/infrastructure/hooks/actions/useNotificationManagementActions.ts +131 -0
  57. package/src/infrastructure/hooks/useNotifications.ts +37 -11
  58. package/src/infrastructure/hooks/utils/useNotificationRefresh.ts +40 -16
  59. package/src/infrastructure/services/NotificationBadgeManager.ts +28 -0
  60. package/src/infrastructure/services/NotificationManager.ts +51 -217
  61. package/src/infrastructure/services/NotificationPermissions.ts +80 -0
  62. package/src/infrastructure/services/NotificationScheduler.ts +77 -0
  63. package/src/infrastructure/services/delivery/NotificationDelivery.ts +32 -14
  64. package/src/infrastructure/storage/NotificationsStore.ts +3 -2
  65. package/src/infrastructure/utils/dev.ts +25 -0
  66. package/src/presentation/screens/NotificationsScreen.tsx +31 -18
  67. package/src/types/global.d.ts +255 -0
@@ -1,8 +1,10 @@
1
- import { useEffect } from 'react';
1
+ import { useEffect, useCallback } from 'react';
2
2
  import { useNotificationsState } from './state/useNotificationsState';
3
3
  import { useNotificationActions } from './actions/useNotificationActions';
4
+ import { useNotificationManagementActions } from './actions/useNotificationManagementActions';
4
5
  import { useNotificationRefresh } from './utils/useNotificationRefresh';
5
6
  import type { UseNotificationsOptions } from './types';
7
+ import { devLog } from '../utils/dev';
6
8
 
7
9
  export * from './types';
8
10
 
@@ -31,10 +33,25 @@ export function useNotifications(userId: string, options: UseNotificationsOption
31
33
  };
32
34
 
33
35
  const actions = useNotificationActions(state, setters);
36
+ const managementActions = useNotificationManagementActions(state, setters);
34
37
  const refresh = useNotificationRefresh(pageSize, setters);
35
38
 
36
- const loadMoreNotifications = () =>
37
- refresh.loadMoreNotifications(state.notifications.length, state.hasMore, state.loading);
39
+ const loadMoreNotifications = useCallback(() =>
40
+ refresh.loadMoreNotifications(state.notifications.length, state.hasMore, state.loading),
41
+ [refresh, state.notifications.length, state.hasMore, state.loading]
42
+ );
43
+
44
+ const cleanup = useCallback(() => {
45
+ setNotifications([]);
46
+ setChannels([]);
47
+ setUnreadCount(0);
48
+ setPreferences(null);
49
+ setError(null);
50
+ setLoading(false);
51
+ setHasMore(true);
52
+
53
+ devLog('[useNotifications] Cleaned up notification state');
54
+ }, [setNotifications, setChannels, setUnreadCount, setPreferences, setError, setLoading, setHasMore]);
38
55
 
39
56
  // Load initial data
40
57
  useEffect(() => {
@@ -43,14 +60,11 @@ export function useNotifications(userId: string, options: UseNotificationsOption
43
60
  refresh.refreshChannels();
44
61
  refresh.refreshPreferences();
45
62
  } else {
46
- setNotifications([]);
47
- setChannels([]);
48
- setUnreadCount(0);
49
- setPreferences(null);
63
+ cleanup();
50
64
  }
51
- }, [userId]);
65
+ }, [userId, cleanup]);
52
66
 
53
- // Auto-refresh setup
67
+ // Auto-refresh setup with proper cleanup
54
68
  useEffect(() => {
55
69
  if (!autoRefresh || !userId) return;
56
70
 
@@ -58,13 +72,25 @@ export function useNotifications(userId: string, options: UseNotificationsOption
58
72
  refresh.refreshNotifications();
59
73
  }, refreshInterval);
60
74
 
61
- return () => clearInterval(interval);
75
+ return () => {
76
+ clearInterval(interval);
77
+ devLog('[useNotifications] Auto-refresh interval cleared');
78
+ };
62
79
  }, [autoRefresh, userId, refreshInterval]);
63
80
 
81
+ // Cleanup on unmount
82
+ useEffect(() => {
83
+ return () => {
84
+ devLog('[useNotifications] Component unmounted, cleaning up');
85
+ };
86
+ }, []);
87
+
64
88
  return {
65
89
  ...state,
66
90
  ...actions,
91
+ ...managementActions,
67
92
  ...refresh,
68
93
  loadMoreNotifications,
94
+ cleanup,
69
95
  };
70
- }
96
+ }
@@ -1,15 +1,10 @@
1
- import { useCallback } from 'react';
1
+ import { useCallback, useRef } from 'react';
2
2
  import AsyncStorage from '@react-native-async-storage/async-storage';
3
3
  import type { Notification } from '../types';
4
4
  import { ChannelManager } from '../../services/channels/ChannelManager';
5
5
  import { PreferencesManager } from '../../services/preferences/PreferencesManager';
6
+ import { devLog, devError } from '../../utils/dev';
6
7
 
7
- /**
8
- * useNotificationRefresh - Offline Notification Refresh
9
- *
10
- * Uses AsyncStorage for local data.
11
- * NO backend - pure offline.
12
- */
13
8
  export const useNotificationRefresh = (pageSize: number, setters: any) => {
14
9
  const {
15
10
  setNotifications,
@@ -21,39 +16,57 @@ export const useNotificationRefresh = (pageSize: number, setters: any) => {
21
16
  setHasMore,
22
17
  } = setters;
23
18
 
24
- const channelManager = new ChannelManager();
25
- const preferencesManager = new PreferencesManager();
19
+ const channelManager = useRef(new ChannelManager()).current;
20
+ const preferencesManager = useRef(new PreferencesManager()).current;
21
+ const abortController = useRef<AbortController | null>(null);
22
+
23
+ const cleanup = useCallback(() => {
24
+ if (abortController.current) {
25
+ abortController.current.abort();
26
+ abortController.current = null;
27
+ }
28
+ }, []);
26
29
 
27
30
  const refreshNotifications = useCallback(async () => {
28
31
  try {
32
+ cleanup();
33
+ abortController.current = new AbortController();
34
+
29
35
  setLoading(true);
30
36
  setError(null);
31
37
 
32
- // Load from AsyncStorage
33
38
  const data = await AsyncStorage.getItem('@notifications:list');
34
39
  const allNotifications: Notification[] = data ? JSON.parse(data) : [];
35
40
 
36
- // Paginate
37
41
  const paginated = allNotifications.slice(0, pageSize);
38
42
  const unread = allNotifications.filter((n) => !n.read).length;
39
43
 
40
44
  setNotifications(paginated);
41
45
  setUnreadCount(unread);
42
46
  setHasMore(allNotifications.length > pageSize);
47
+
48
+ devLog('[useNotificationRefresh] Refreshed notifications:', paginated.length);
43
49
  } catch (err) {
50
+ if (err instanceof Error && err.name === 'AbortError') {
51
+ return;
52
+ }
44
53
  setError(
45
54
  err instanceof Error ? err.message : 'Failed to load notifications'
46
55
  );
47
56
  } finally {
48
57
  setLoading(false);
58
+ cleanup();
49
59
  }
50
- }, [pageSize, setNotifications, setUnreadCount, setHasMore, setLoading, setError]);
60
+ }, [pageSize, setNotifications, setUnreadCount, setHasMore, setLoading, setError, cleanup]);
51
61
 
52
62
  const loadMoreNotifications = useCallback(
53
63
  async (currentLength: number, hasMore: boolean, loading: boolean) => {
54
64
  if (!hasMore || loading) return;
55
65
 
56
66
  try {
67
+ cleanup();
68
+ abortController.current = new AbortController();
69
+
57
70
  setLoading(true);
58
71
  setError(null);
59
72
 
@@ -67,7 +80,12 @@ export const useNotificationRefresh = (pageSize: number, setters: any) => {
67
80
 
68
81
  setNotifications((prev: any[]) => [...prev, ...moreNotifications]);
69
82
  setHasMore(allNotifications.length > currentLength + pageSize);
83
+
84
+ devLog('[useNotificationRefresh] Loaded more notifications:', moreNotifications.length);
70
85
  } catch (err) {
86
+ if (err instanceof Error && err.name === 'AbortError') {
87
+ return;
88
+ }
71
89
  setError(
72
90
  err instanceof Error
73
91
  ? err.message
@@ -75,17 +93,20 @@ export const useNotificationRefresh = (pageSize: number, setters: any) => {
75
93
  );
76
94
  } finally {
77
95
  setLoading(false);
96
+ cleanup();
78
97
  }
79
98
  },
80
- [pageSize, setNotifications, setHasMore, setLoading, setError]
99
+ [pageSize, setNotifications, setHasMore, setLoading, setError, cleanup]
81
100
  );
82
101
 
83
102
  const refreshChannels = useCallback(async () => {
84
103
  try {
85
104
  const channelsData = await channelManager.getActiveChannels();
86
105
  setChannels(channelsData);
106
+
107
+ devLog('[useNotificationRefresh] Refreshed channels:', channelsData.length);
87
108
  } catch (err) {
88
- // Silent failure
109
+ devError('[useNotificationRefresh] Failed to refresh channels:', err);
89
110
  }
90
111
  }, [setChannels]);
91
112
 
@@ -93,8 +114,10 @@ export const useNotificationRefresh = (pageSize: number, setters: any) => {
93
114
  try {
94
115
  const prefsData = await preferencesManager.get();
95
116
  setPreferences(prefsData);
117
+
118
+ devLog('[useNotificationRefresh] Refreshed preferences');
96
119
  } catch (err) {
97
- // Silent failure
120
+ devError('[useNotificationRefresh] Failed to refresh preferences:', err);
98
121
  }
99
122
  }, [setPreferences]);
100
123
 
@@ -103,5 +126,6 @@ export const useNotificationRefresh = (pageSize: number, setters: any) => {
103
126
  loadMoreNotifications,
104
127
  refreshChannels,
105
128
  refreshPreferences,
129
+ cleanup,
106
130
  };
107
- };
131
+ };
@@ -0,0 +1,28 @@
1
+ import * as Notifications from 'expo-notifications';
2
+ import { Platform } from 'react-native';
3
+ import { devError } from '../utils/dev';
4
+
5
+ export class NotificationBadgeManager {
6
+ async getBadgeCount(): Promise<number> {
7
+ try {
8
+ if (Platform.OS === 'ios') {
9
+ return await Notifications.getBadgeCountAsync();
10
+ }
11
+ return 0;
12
+ } catch (error) {
13
+ devError('[NotificationBadgeManager] Get badge count failed:', error);
14
+ return 0;
15
+ }
16
+ }
17
+
18
+ async setBadgeCount(count: number): Promise<void> {
19
+ try {
20
+ if (Platform.OS === 'ios') {
21
+ await Notifications.setBadgeCountAsync(count);
22
+ }
23
+ } catch (error) {
24
+ devError('[NotificationBadgeManager] Set badge count failed:', error);
25
+ throw error;
26
+ }
27
+ }
28
+ }
@@ -1,42 +1,24 @@
1
1
  /**
2
- * NotificationManager
2
+ * NotificationManager - Core Notification Operations
3
3
  *
4
4
  * Offline-first notification system using expo-notifications.
5
5
  * Works in ALL apps (offline, online, hybrid) - no backend required.
6
- *
7
- * Features:
8
- * - Schedule notifications for specific dates/times
9
- * - Repeating notifications (daily, weekly, monthly)
10
- * - Android notification channels
11
- * - Permission handling
12
- * - Cancel individual or all notifications
13
- * - Works completely offline
14
- *
15
- * Use Cases:
16
- * - Reminders (bills, tasks, appointments)
17
- * - Habit tracking (daily streaks)
18
- * - Medication reminders
19
- * - Any app needing local notifications
20
- *
21
- * @module NotificationManager
22
6
  */
23
7
 
24
8
  import * as Notifications from 'expo-notifications';
25
9
  import * as Device from 'expo-device';
26
10
  import { Platform } from 'react-native';
11
+ import { NotificationPermissions } from './NotificationPermissions';
12
+ import { NotificationScheduler } from './NotificationScheduler';
13
+ import { NotificationBadgeManager } from './NotificationBadgeManager';
14
+ import { devLog, devError } from '../utils/dev';
27
15
 
28
- /**
29
- * Trigger types for notifications
30
- */
31
16
  export type NotificationTrigger =
32
17
  | { type: 'date'; date: Date }
33
18
  | { type: 'daily'; hour: number; minute: number }
34
19
  | { type: 'weekly'; weekday: number; hour: number; minute: number }
35
20
  | { type: 'monthly'; day: number; hour: number; minute: number };
36
21
 
37
- /**
38
- * Options for scheduling a notification
39
- */
40
22
  export interface ScheduleNotificationOptions {
41
23
  title: string;
42
24
  body: string;
@@ -47,9 +29,6 @@ export interface ScheduleNotificationOptions {
47
29
  categoryIdentifier?: string;
48
30
  }
49
31
 
50
- /**
51
- * Scheduled notification details
52
- */
53
32
  export interface ScheduledNotification {
54
33
  identifier: string;
55
34
  content: {
@@ -60,16 +39,17 @@ export interface ScheduledNotification {
60
39
  trigger: any;
61
40
  }
62
41
 
63
- /**
64
- * NotificationManager
65
- *
66
- * Handles all notification operations using expo-notifications.
67
- * Works completely offline - no backend, no user IDs, just device-local notifications.
68
- */
69
42
  export class NotificationManager {
70
- /**
71
- * Configure notification handler (how notifications appear when app is in foreground)
72
- */
43
+ private permissions: NotificationPermissions;
44
+ private scheduler: NotificationScheduler;
45
+ private badgeManager: NotificationBadgeManager;
46
+
47
+ constructor() {
48
+ this.permissions = new NotificationPermissions();
49
+ this.scheduler = new NotificationScheduler();
50
+ this.badgeManager = new NotificationBadgeManager();
51
+ }
52
+
73
53
  static configure() {
74
54
  Notifications.setNotificationHandler({
75
55
  handleNotification: async () => ({
@@ -78,250 +58,104 @@ export class NotificationManager {
78
58
  shouldSetBadge: true,
79
59
  }),
80
60
  });
61
+
62
+ devLog('[NotificationManager] Configured notification handler');
81
63
  }
82
64
 
83
- /**
84
- * Request notification permissions
85
- * iOS: Shows system permission dialog
86
- * Android: Permissions granted by default (Android 13+ requires runtime permission)
87
- */
88
65
  async requestPermissions(): Promise<boolean> {
89
66
  try {
90
- if (!Device.isDevice) {
91
- console.warn('[NotificationManager] Notifications only work on physical devices');
92
- return false;
93
- }
94
-
95
- const permissionsResponse = await Notifications.getPermissionsAsync();
96
- const existingStatus = (permissionsResponse as any).status || ((permissionsResponse as any).granted ? 'granted' : 'denied');
97
- let finalStatus = existingStatus;
98
-
99
- if (existingStatus !== 'granted') {
100
- const requestResponse = await Notifications.requestPermissionsAsync();
101
- finalStatus = (requestResponse as any).status || ((requestResponse as any).granted ? 'granted' : 'denied');
102
- }
103
-
104
- if (Platform.OS === 'android') {
105
- await this.createAndroidChannels();
106
- }
107
-
108
- return finalStatus === 'granted';
67
+ const result = await this.permissions.requestPermissions();
68
+
69
+ devLog('[NotificationManager] Permissions requested:', result);
70
+
71
+ return result;
109
72
  } catch (error) {
110
- console.error('[NotificationManager] Permission request failed:', error);
73
+ devError('[NotificationManager] Permission request failed:', error);
111
74
  return false;
112
75
  }
113
76
  }
114
77
 
115
- /**
116
- * Check if notification permissions are granted
117
- */
118
78
  async hasPermissions(): Promise<boolean> {
119
79
  try {
120
- if (!Device.isDevice) return false;
121
- const permissionsResponse = await Notifications.getPermissionsAsync();
122
- return (permissionsResponse as any).status === 'granted' || (permissionsResponse as any).granted === true;
80
+ return await this.permissions.hasPermissions();
123
81
  } catch (error) {
124
- console.error('[NotificationManager] Permission check failed:', error);
82
+ devError('[NotificationManager] Permission check failed:', error);
125
83
  return false;
126
84
  }
127
85
  }
128
86
 
129
- /**
130
- * Create Android notification channels (required for Android 8+)
131
- */
132
- private async createAndroidChannels(): Promise<void> {
133
- if (Platform.OS !== 'android') return;
134
-
135
- try {
136
- await Notifications.setNotificationChannelAsync('default', {
137
- name: 'Default',
138
- importance: Notifications.AndroidImportance.DEFAULT,
139
- vibrationPattern: [0, 250, 250, 250],
140
- sound: 'default',
141
- lightColor: '#3B82F6',
142
- });
143
-
144
- await Notifications.setNotificationChannelAsync('reminders', {
145
- name: 'Reminders',
146
- importance: Notifications.AndroidImportance.HIGH,
147
- vibrationPattern: [0, 250, 250, 250],
148
- sound: 'default',
149
- lightColor: '#3B82F6',
150
- enableVibrate: true,
151
- });
152
-
153
- await Notifications.setNotificationChannelAsync('urgent', {
154
- name: 'Urgent',
155
- importance: Notifications.AndroidImportance.MAX,
156
- vibrationPattern: [0, 500, 250, 500],
157
- sound: 'default',
158
- lightColor: '#EF4444',
159
- enableVibrate: true,
160
- });
161
- } catch (error) {
162
- console.error('[NotificationManager] Android channel creation failed:', error);
163
- }
164
- }
165
-
166
- /**
167
- * Schedule a notification
168
- *
169
- * @example
170
- * // Specific date
171
- * const id = await manager.scheduleNotification({
172
- * title: 'Bill Reminder',
173
- * body: 'Electricity bill due today',
174
- * trigger: { type: 'date', date: new Date('2025-01-15T09:00:00') }
175
- * });
176
- *
177
- * @example
178
- * // Daily reminder
179
- * const id = await manager.scheduleNotification({
180
- * title: 'Daily Workout',
181
- * body: 'Time for your morning workout!',
182
- * trigger: { type: 'daily', hour: 7, minute: 0 }
183
- * });
184
- */
185
87
  async scheduleNotification(options: ScheduleNotificationOptions): Promise<string> {
186
88
  try {
187
- const { title, body, data = {}, trigger, sound = true, badge, categoryIdentifier } = options;
188
-
189
- let notificationTrigger: any;
190
-
191
- if (trigger.type === 'date') {
192
- notificationTrigger = {
193
- date: trigger.date,
194
- channelId: categoryIdentifier || 'default',
195
- };
196
- } else if (trigger.type === 'daily') {
197
- notificationTrigger = {
198
- hour: trigger.hour,
199
- minute: trigger.minute,
200
- repeats: true,
201
- channelId: categoryIdentifier || 'reminders',
202
- };
203
- } else if (trigger.type === 'weekly') {
204
- notificationTrigger = {
205
- weekday: trigger.weekday,
206
- hour: trigger.hour,
207
- minute: trigger.minute,
208
- repeats: true,
209
- channelId: categoryIdentifier || 'reminders',
210
- };
211
- } else if (trigger.type === 'monthly') {
212
- notificationTrigger = {
213
- day: trigger.day,
214
- hour: trigger.hour,
215
- minute: trigger.minute,
216
- repeats: true,
217
- channelId: categoryIdentifier || 'reminders',
218
- };
219
- }
220
-
221
- const notificationId = await Notifications.scheduleNotificationAsync({
222
- content: {
223
- title,
224
- body,
225
- data,
226
- sound: sound === true ? 'default' : sound || undefined,
227
- badge,
228
- categoryIdentifier,
229
- priority: Notifications.AndroidNotificationPriority.HIGH,
230
- vibrate: [0, 250, 250, 250],
231
- },
232
- trigger: notificationTrigger,
233
- });
234
-
235
- return notificationId;
89
+ const id = await this.scheduler.scheduleNotification(options);
90
+
91
+ devLog('[NotificationManager] Notification scheduled:', id);
92
+
93
+ return id;
236
94
  } catch (error) {
237
- console.error('[NotificationManager] Schedule notification failed:', error);
95
+ devError('[NotificationManager] Schedule notification failed:', error);
238
96
  throw error;
239
97
  }
240
98
  }
241
99
 
242
- /**
243
- * Cancel a scheduled notification
244
- */
245
100
  async cancelNotification(notificationId: string): Promise<void> {
246
101
  try {
247
- await Notifications.cancelScheduledNotificationAsync(notificationId);
102
+ await this.scheduler.cancelNotification(notificationId);
103
+
104
+ devLog('[NotificationManager] Notification cancelled:', notificationId);
248
105
  } catch (error) {
249
- console.error('[NotificationManager] Cancel notification failed:', error);
106
+ devError('[NotificationManager] Cancel notification failed:', error);
250
107
  throw error;
251
108
  }
252
109
  }
253
110
 
254
- /**
255
- * Cancel all scheduled notifications
256
- */
257
111
  async cancelAllNotifications(): Promise<void> {
258
112
  try {
259
- await Notifications.cancelAllScheduledNotificationsAsync();
113
+ await this.scheduler.cancelAllNotifications();
114
+
115
+ devLog('[NotificationManager] All notifications cancelled');
260
116
  } catch (error) {
261
- console.error('[NotificationManager] Cancel all notifications failed:', error);
117
+ devError('[NotificationManager] Cancel all notifications failed:', error);
262
118
  throw error;
263
119
  }
264
120
  }
265
121
 
266
- /**
267
- * Get all scheduled notifications
268
- */
269
122
  async getScheduledNotifications(): Promise<ScheduledNotification[]> {
270
123
  try {
271
- const notifications = await Notifications.getAllScheduledNotificationsAsync();
272
- return notifications.map(notification => ({
273
- identifier: notification.identifier,
274
- content: {
275
- title: notification.content.title || '',
276
- body: notification.content.body || '',
277
- data: notification.content.data as Record<string, any>,
278
- },
279
- trigger: notification.trigger,
280
- }));
124
+ return await this.scheduler.getScheduledNotifications();
281
125
  } catch (error) {
282
- console.error('[NotificationManager] Get scheduled notifications failed:', error);
126
+ devError('[NotificationManager] Get scheduled notifications failed:', error);
283
127
  return [];
284
128
  }
285
129
  }
286
130
 
287
- /**
288
- * Dismiss all delivered notifications (clear from notification center)
289
- */
290
131
  async dismissAllNotifications(): Promise<void> {
291
132
  try {
292
133
  await Notifications.dismissAllNotificationsAsync();
134
+
135
+ devLog('[NotificationManager] All notifications dismissed');
293
136
  } catch (error) {
294
- console.error('[NotificationManager] Dismiss all notifications failed:', error);
137
+ devError('[NotificationManager] Dismiss all notifications failed:', error);
295
138
  throw error;
296
139
  }
297
140
  }
298
141
 
299
- /**
300
- * Get notification badge count (iOS only)
301
- */
302
142
  async getBadgeCount(): Promise<number> {
303
143
  try {
304
- if (Platform.OS === 'ios') {
305
- return await Notifications.getBadgeCountAsync();
306
- }
307
- return 0;
144
+ return await this.badgeManager.getBadgeCount();
308
145
  } catch (error) {
309
- console.error('[NotificationManager] Get badge count failed:', error);
146
+ devError('[NotificationManager] Get badge count failed:', error);
310
147
  return 0;
311
148
  }
312
149
  }
313
150
 
314
- /**
315
- * Set notification badge count (iOS only)
316
- */
317
151
  async setBadgeCount(count: number): Promise<void> {
318
152
  try {
319
- if (Platform.OS === 'ios') {
320
- await Notifications.setBadgeCountAsync(count);
321
- }
153
+ await this.badgeManager.setBadgeCount(count);
154
+
155
+ devLog('[NotificationManager] Badge count set:', count);
322
156
  } catch (error) {
323
- console.error('[NotificationManager] Set badge count failed:', error);
157
+ devError('[NotificationManager] Set badge count failed:', error);
324
158
  throw error;
325
159
  }
326
160
  }
327
- }
161
+ }