@umituz/react-native-notifications 1.0.5 → 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 (72) hide show
  1. package/lib/index.d.ts +1 -0
  2. package/lib/index.d.ts.map +1 -1
  3. package/lib/index.js +5 -0
  4. package/lib/index.js.map +1 -1
  5. package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts +4 -13
  6. package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts.map +1 -1
  7. package/lib/infrastructure/hooks/actions/useNotificationActions.js +4 -70
  8. package/lib/infrastructure/hooks/actions/useNotificationActions.js.map +1 -1
  9. package/lib/infrastructure/hooks/actions/useNotificationManagementActions.d.ts +8 -0
  10. package/lib/infrastructure/hooks/actions/useNotificationManagementActions.d.ts.map +1 -0
  11. package/lib/infrastructure/hooks/actions/useNotificationManagementActions.js +78 -0
  12. package/lib/infrastructure/hooks/actions/useNotificationManagementActions.js.map +1 -0
  13. package/lib/infrastructure/hooks/state/useNotificationsState.d.ts +8 -8
  14. package/lib/infrastructure/hooks/useNotificationSettings.d.ts +2 -2
  15. package/lib/infrastructure/hooks/useNotifications.d.ts +21 -1
  16. package/lib/infrastructure/hooks/useNotifications.d.ts.map +1 -1
  17. package/lib/infrastructure/hooks/useNotifications.js +30 -9
  18. package/lib/infrastructure/hooks/useNotifications.js.map +1 -1
  19. package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts +5 -10
  20. package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts.map +1 -1
  21. package/lib/infrastructure/hooks/utils/useNotificationRefresh.js +32 -15
  22. package/lib/infrastructure/hooks/utils/useNotificationRefresh.js.map +1 -1
  23. package/lib/infrastructure/services/NotificationBadgeManager.d.ts +5 -0
  24. package/lib/infrastructure/services/NotificationBadgeManager.d.ts.map +1 -0
  25. package/lib/infrastructure/services/NotificationBadgeManager.js +29 -0
  26. package/lib/infrastructure/services/NotificationBadgeManager.js.map +1 -0
  27. package/lib/infrastructure/services/NotificationManager.d.ts +5 -84
  28. package/lib/infrastructure/services/NotificationManager.d.ts.map +1 -1
  29. package/lib/infrastructure/services/NotificationManager.js +36 -203
  30. package/lib/infrastructure/services/NotificationManager.js.map +1 -1
  31. package/lib/infrastructure/services/NotificationPermissions.d.ts +6 -0
  32. package/lib/infrastructure/services/NotificationPermissions.d.ts.map +1 -0
  33. package/lib/infrastructure/services/NotificationPermissions.js +75 -0
  34. package/lib/infrastructure/services/NotificationPermissions.js.map +1 -0
  35. package/lib/infrastructure/services/NotificationScheduler.d.ts +8 -0
  36. package/lib/infrastructure/services/NotificationScheduler.d.ts.map +1 -0
  37. package/lib/infrastructure/services/NotificationScheduler.js +72 -0
  38. package/lib/infrastructure/services/NotificationScheduler.js.map +1 -0
  39. package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts +2 -8
  40. package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts.map +1 -1
  41. package/lib/infrastructure/services/delivery/NotificationDelivery.js +27 -13
  42. package/lib/infrastructure/services/delivery/NotificationDelivery.js.map +1 -1
  43. package/lib/infrastructure/storage/NotificationsStore.d.ts +8 -1
  44. package/lib/infrastructure/storage/NotificationsStore.d.ts.map +1 -1
  45. package/lib/infrastructure/storage/NotificationsStore.js +2 -1
  46. package/lib/infrastructure/storage/NotificationsStore.js.map +1 -1
  47. package/lib/infrastructure/utils/dev.d.ts +5 -0
  48. package/lib/infrastructure/utils/dev.d.ts.map +1 -0
  49. package/lib/infrastructure/utils/dev.js +24 -0
  50. package/lib/infrastructure/utils/dev.js.map +1 -0
  51. package/lib/presentation/screens/NotificationsScreen.d.ts +14 -4
  52. package/lib/presentation/screens/NotificationsScreen.d.ts.map +1 -1
  53. package/lib/presentation/screens/NotificationsScreen.js +12 -15
  54. package/lib/presentation/screens/NotificationsScreen.js.map +1 -1
  55. package/package.json +2 -2
  56. package/src/__tests__/NotificationManager.test.ts +215 -0
  57. package/src/__tests__/useNotificationActions.test.ts +189 -0
  58. package/src/__tests__/useNotificationRefresh.test.ts +213 -0
  59. package/src/index.ts +7 -0
  60. package/src/infrastructure/hooks/actions/useNotificationActions.ts +8 -110
  61. package/src/infrastructure/hooks/actions/useNotificationManagementActions.ts +131 -0
  62. package/src/infrastructure/hooks/useNotifications.ts +37 -11
  63. package/src/infrastructure/hooks/utils/useNotificationRefresh.ts +40 -16
  64. package/src/infrastructure/services/NotificationBadgeManager.ts +28 -0
  65. package/src/infrastructure/services/NotificationManager.ts +51 -217
  66. package/src/infrastructure/services/NotificationPermissions.ts +80 -0
  67. package/src/infrastructure/services/NotificationScheduler.ts +77 -0
  68. package/src/infrastructure/services/delivery/NotificationDelivery.ts +32 -14
  69. package/src/infrastructure/storage/NotificationsStore.ts +3 -2
  70. package/src/infrastructure/utils/dev.ts +25 -0
  71. package/src/presentation/screens/NotificationsScreen.tsx +31 -18
  72. package/src/types/global.d.ts +255 -0
@@ -0,0 +1,189 @@
1
+ import { renderHook, act } from '@testing-library/react-hooks';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+ import { useNotificationActions } from '../src/infrastructure/hooks/actions/useNotificationActions';
4
+ import { useNotificationsState } from '../src/infrastructure/hooks/state/useNotificationsState';
5
+
6
+ // Mock AsyncStorage
7
+ jest.mock('@react-native-async-storage/async-storage', () => ({
8
+ getItem: jest.fn(),
9
+ setItem: jest.fn(),
10
+ }));
11
+
12
+ // Mock expo-notifications
13
+ jest.mock('expo-notifications', () => ({
14
+ scheduleNotificationAsync: jest.fn(),
15
+ }));
16
+
17
+ // Mock services
18
+ jest.mock('../src/infrastructure/services/delivery/NotificationDelivery', () => ({
19
+ NotificationDelivery: jest.fn().mockImplementation(() => ({
20
+ deliver: jest.fn().mockResolvedValue(undefined),
21
+ })),
22
+ }));
23
+
24
+ jest.mock('../src/infrastructure/services/channels/ChannelManager', () => ({
25
+ ChannelManager: jest.fn().mockImplementation(() => ({
26
+ register: jest.fn(),
27
+ verify: jest.fn(),
28
+ })),
29
+ }));
30
+
31
+ jest.mock('../src/infrastructure/services/preferences/PreferencesManager', () => ({
32
+ PreferencesManager: jest.fn().mockImplementation(() => ({
33
+ update: jest.fn().mockResolvedValue(true),
34
+ })),
35
+ }));
36
+
37
+ describe('useNotificationActions', () => {
38
+ let mockState: any;
39
+ let mockSetters: any;
40
+
41
+ beforeEach(() => {
42
+ jest.clearAllMocks();
43
+
44
+ mockState = {
45
+ notifications: [],
46
+ channels: [],
47
+ unreadCount: 0,
48
+ preferences: null,
49
+ loading: false,
50
+ error: null,
51
+ hasMore: true,
52
+ };
53
+
54
+ mockSetters = {
55
+ setNotifications: jest.fn(),
56
+ setChannels: jest.fn(),
57
+ setUnreadCount: jest.fn(),
58
+ setPreferences: jest.fn(),
59
+ setLoading: jest.fn(),
60
+ setError: jest.fn(),
61
+ setHasMore: jest.fn(),
62
+ };
63
+
64
+ (AsyncStorage.getItem as jest.Mock).mockResolvedValue('[]');
65
+ (AsyncStorage.setItem as jest.Mock).mockResolvedValue(undefined);
66
+ });
67
+
68
+ describe('sendNotification', () => {
69
+ it('should send a notification successfully', async () => {
70
+ const { result } = renderHook(() => useNotificationActions(mockState, mockSetters));
71
+
72
+ const options = {
73
+ title: 'Test Notification',
74
+ body: 'Test body',
75
+ data: { key: 'value' },
76
+ };
77
+
78
+ let resultNotifications: any[] = [];
79
+ await act(async () => {
80
+ resultNotifications = await result.current.sendNotification(options);
81
+ });
82
+
83
+ expect(resultNotifications).toHaveLength(1);
84
+ expect(resultNotifications[0]).toMatchObject({
85
+ title: 'Test Notification',
86
+ body: 'Test body',
87
+ data: { key: 'value' },
88
+ read: false,
89
+ });
90
+ expect(AsyncStorage.setItem).toHaveBeenCalled();
91
+ expect(mockSetters.setError).toHaveBeenCalledWith(null);
92
+ });
93
+
94
+ it('should handle send notification errors', async () => {
95
+ (AsyncStorage.setItem as jest.Mock).mockRejectedValue(new Error('Storage error'));
96
+ const { result } = renderHook(() => useNotificationActions(mockState, mockSetters));
97
+
98
+ const options = {
99
+ title: 'Test',
100
+ body: 'Test',
101
+ };
102
+
103
+ let resultNotifications: any[] = [];
104
+ await act(async () => {
105
+ resultNotifications = await result.current.sendNotification(options);
106
+ });
107
+
108
+ expect(resultNotifications).toHaveLength(0);
109
+ expect(mockSetters.setError).toHaveBeenCalledWith('Storage error');
110
+ });
111
+
112
+ it('should schedule notification with date', async () => {
113
+ const { result } = renderHook(() => useNotificationActions(mockState, mockSetters));
114
+
115
+ const scheduledDate = new Date('2025-01-15T09:00:00');
116
+ const options = {
117
+ title: 'Scheduled Notification',
118
+ body: 'Test body',
119
+ scheduled_for: scheduledDate,
120
+ };
121
+
122
+ await act(async () => {
123
+ await result.current.sendNotification(options);
124
+ });
125
+
126
+ const notificationData = JSON.parse((AsyncStorage.setItem as jest.Mock).mock.calls[0][1]);
127
+ expect(notificationData[0]).toMatchObject({
128
+ scheduled_for: scheduledDate.toISOString(),
129
+ });
130
+ });
131
+ });
132
+
133
+ describe('markAsRead', () => {
134
+ it('should mark notification as read successfully', async () => {
135
+ const mockNotifications = [
136
+ { id: 'notif-1', title: 'Test 1', read: false },
137
+ { id: 'notif-2', title: 'Test 2', read: false },
138
+ ];
139
+ (AsyncStorage.getItem as jest.Mock).mockResolvedValue(JSON.stringify(mockNotifications));
140
+
141
+ const { result } = renderHook(() => useNotificationActions(mockState, mockSetters));
142
+
143
+ let success = false;
144
+ await act(async () => {
145
+ success = await result.current.markAsRead('notif-1');
146
+ });
147
+
148
+ expect(success).toBe(true);
149
+ expect(AsyncStorage.setItem).toHaveBeenCalled();
150
+ expect(mockSetters.setUnreadCount).toHaveBeenCalledWith(0);
151
+ });
152
+
153
+ it('should handle mark as read errors', async () => {
154
+ (AsyncStorage.getItem as jest.Mock).mockRejectedValue(new Error('Read error'));
155
+ const { result } = renderHook(() => useNotificationActions(mockState, mockSetters));
156
+
157
+ let success = false;
158
+ await act(async () => {
159
+ success = await result.current.markAsRead('notif-1');
160
+ });
161
+
162
+ expect(success).toBe(false);
163
+ expect(mockSetters.setError).toHaveBeenCalledWith('Read error');
164
+ });
165
+ });
166
+
167
+ describe('markAllAsRead', () => {
168
+ it('should mark all notifications as read', async () => {
169
+ const mockNotifications = [
170
+ { id: 'notif-1', title: 'Test 1', read: false },
171
+ { id: 'notif-2', title: 'Test 2', read: false },
172
+ ];
173
+ (AsyncStorage.getItem as jest.Mock).mockResolvedValue(JSON.stringify(mockNotifications));
174
+
175
+ const { result } = renderHook(() => useNotificationActions(mockState, mockSetters));
176
+
177
+ let success = false;
178
+ await act(async () => {
179
+ success = await result.current.markAllAsRead();
180
+ });
181
+
182
+ expect(success).toBe(true);
183
+ expect(mockSetters.setUnreadCount).toHaveBeenCalledWith(0);
184
+
185
+ const savedData = JSON.parse((AsyncStorage.setItem as jest.Mock).mock.calls[0][1]);
186
+ expect(savedData.every((n: any) => n.read)).toBe(true);
187
+ });
188
+ });
189
+ });
@@ -0,0 +1,213 @@
1
+ import { renderHook, act } from '@testing-library/react-hooks';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+ import { useNotificationRefresh } from '../src/infrastructure/hooks/utils/useNotificationRefresh';
4
+
5
+ // Mock AsyncStorage
6
+ jest.mock('@react-native-async-storage/async-storage', () => ({
7
+ getItem: jest.fn(),
8
+ setItem: jest.fn(),
9
+ }));
10
+
11
+ // Mock services
12
+ jest.mock('../src/infrastructure/services/channels/ChannelManager', () => ({
13
+ ChannelManager: jest.fn().mockImplementation(() => ({
14
+ getActiveChannels: jest.fn().mockResolvedValue([]),
15
+ })),
16
+ }));
17
+
18
+ jest.mock('../src/infrastructure/services/preferences/PreferencesManager', () => ({
19
+ PreferencesManager: jest.fn().mockImplementation(() => ({
20
+ get: jest.fn().mockResolvedValue(null),
21
+ })),
22
+ }));
23
+
24
+ describe('useNotificationRefresh', () => {
25
+ let mockSetters: any;
26
+
27
+ beforeEach(() => {
28
+ jest.clearAllMocks();
29
+
30
+ mockSetters = {
31
+ setNotifications: jest.fn(),
32
+ setChannels: jest.fn(),
33
+ setUnreadCount: jest.fn(),
34
+ setPreferences: jest.fn(),
35
+ setLoading: jest.fn(),
36
+ setError: jest.fn(),
37
+ setHasMore: jest.fn(),
38
+ };
39
+
40
+ (AsyncStorage.getItem as jest.Mock).mockResolvedValue('[]');
41
+ });
42
+
43
+ describe('refreshNotifications', () => {
44
+ it('should refresh notifications successfully', async () => {
45
+ const mockNotifications = [
46
+ { id: 'notif-1', title: 'Test 1', read: false },
47
+ { id: 'notif-2', title: 'Test 2', read: true },
48
+ { id: 'notif-3', title: 'Test 3', read: false },
49
+ ];
50
+ (AsyncStorage.getItem as jest.Mock).mockResolvedValue(JSON.stringify(mockNotifications));
51
+
52
+ const { result } = renderHook(() => useNotificationRefresh(2, mockSetters));
53
+
54
+ await act(async () => {
55
+ await result.current.refreshNotifications();
56
+ });
57
+
58
+ expect(mockSetters.setLoading).toHaveBeenCalledWith(true);
59
+ expect(mockSetters.setError).toHaveBeenCalledWith(null);
60
+ expect(mockSetters.setNotifications).toHaveBeenCalledWith(mockNotifications.slice(0, 2));
61
+ expect(mockSetters.setUnreadCount).toHaveBeenCalledWith(2);
62
+ expect(mockSetters.setHasMore).toHaveBeenCalledWith(true);
63
+ expect(mockSetters.setLoading).toHaveBeenCalledWith(false);
64
+ });
65
+
66
+ it('should handle empty notifications list', async () => {
67
+ (AsyncStorage.getItem as jest.Mock).mockResolvedValue('[]');
68
+
69
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
70
+
71
+ await act(async () => {
72
+ await result.current.refreshNotifications();
73
+ });
74
+
75
+ expect(mockSetters.setNotifications).toHaveBeenCalledWith([]);
76
+ expect(mockSetters.setUnreadCount).toHaveBeenCalledWith(0);
77
+ expect(mockSetters.setHasMore).toHaveBeenCalledWith(false);
78
+ });
79
+
80
+ it('should handle refresh errors', async () => {
81
+ (AsyncStorage.getItem as jest.Mock).mockRejectedValue(new Error('Refresh error'));
82
+
83
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
84
+
85
+ await act(async () => {
86
+ await result.current.refreshNotifications();
87
+ });
88
+
89
+ expect(mockSetters.setError).toHaveBeenCalledWith('Refresh error');
90
+ expect(mockSetters.setLoading).toHaveBeenCalledWith(false);
91
+ });
92
+ });
93
+
94
+ describe('loadMoreNotifications', () => {
95
+ it('should load more notifications', async () => {
96
+ const mockNotifications = Array.from({ length: 15 }, (_, i) => ({
97
+ id: `notif-${i}`,
98
+ title: `Test ${i}`,
99
+ read: false,
100
+ }));
101
+ (AsyncStorage.getItem as jest.Mock).mockResolvedValue(JSON.stringify(mockNotifications));
102
+
103
+ const { result } = renderHook(() => useNotificationRefresh(5, mockSetters));
104
+
105
+ // First load
106
+ await act(async () => {
107
+ await result.current.refreshNotifications();
108
+ });
109
+
110
+ // Load more
111
+ await act(async () => {
112
+ await result.current.loadMoreNotifications(5, true, false);
113
+ });
114
+
115
+ expect(mockSetters.setNotifications).toHaveBeenCalledTimes(2);
116
+ expect(mockSetters.setHasMore).toHaveBeenCalledWith(true);
117
+ });
118
+
119
+ it('should not load more if no more notifications', async () => {
120
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
121
+
122
+ await act(async () => {
123
+ await result.current.loadMoreNotifications(5, false, false);
124
+ });
125
+
126
+ expect(mockSetters.setNotifications).not.toHaveBeenCalled();
127
+ });
128
+
129
+ it('should not load more if already loading', async () => {
130
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
131
+
132
+ await act(async () => {
133
+ await result.current.loadMoreNotifications(5, true, true);
134
+ });
135
+
136
+ expect(mockSetters.setNotifications).not.toHaveBeenCalled();
137
+ });
138
+ });
139
+
140
+ describe('refreshChannels', () => {
141
+ it('should refresh channels successfully', async () => {
142
+ const mockChannels = [
143
+ { id: 'channel-1', name: 'Channel 1' },
144
+ { id: 'channel-2', name: 'Channel 2' },
145
+ ];
146
+
147
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
148
+
149
+ await act(async () => {
150
+ await result.current.refreshChannels();
151
+ });
152
+
153
+ expect(mockSetters.setChannels).toHaveBeenCalledWith(mockChannels);
154
+ });
155
+
156
+ it('should handle channel refresh errors silently', async () => {
157
+ const ChannelManager = require('../src/infrastructure/services/channels/ChannelManager').ChannelManager;
158
+ ChannelManager.mockImplementation(() => ({
159
+ getActiveChannels: jest.fn().mockRejectedValue(new Error('Channel error')),
160
+ }));
161
+
162
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
163
+
164
+ await act(async () => {
165
+ await result.current.refreshChannels();
166
+ });
167
+
168
+ expect(mockSetters.setChannels).not.toHaveBeenCalled();
169
+ });
170
+ });
171
+
172
+ describe('refreshPreferences', () => {
173
+ it('should refresh preferences successfully', async () => {
174
+ const mockPreferences = { enabled: true, sound: false };
175
+
176
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
177
+
178
+ await act(async () => {
179
+ await result.current.refreshPreferences();
180
+ });
181
+
182
+ expect(mockSetters.setPreferences).toHaveBeenCalledWith(mockPreferences);
183
+ });
184
+
185
+ it('should handle preferences refresh errors silently', async () => {
186
+ const PreferencesManager = require('../src/infrastructure/services/preferences/PreferencesManager').PreferencesManager;
187
+ PreferencesManager.mockImplementation(() => ({
188
+ get: jest.fn().mockRejectedValue(new Error('Preferences error')),
189
+ }));
190
+
191
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
192
+
193
+ await act(async () => {
194
+ await result.current.refreshPreferences();
195
+ });
196
+
197
+ expect(mockSetters.setPreferences).not.toHaveBeenCalled();
198
+ });
199
+ });
200
+
201
+ describe('cleanup', () => {
202
+ it('should cleanup properly', async () => {
203
+ const { result } = renderHook(() => useNotificationRefresh(10, mockSetters));
204
+
205
+ await act(async () => {
206
+ result.current.cleanup();
207
+ });
208
+
209
+ // Should not throw any errors
210
+ expect(true).toBe(true);
211
+ });
212
+ });
213
+ });
package/src/index.ts CHANGED
@@ -32,3 +32,10 @@ export { NotificationManager } from './infrastructure/services/NotificationManag
32
32
 
33
33
  // Hooks
34
34
  export { useNotificationSettings } from './infrastructure/hooks/useNotificationSettings';
35
+
36
+ // ============================================================================
37
+ // PRESENTATION LAYER EXPORTS
38
+ // ============================================================================
39
+
40
+ // Screens
41
+ export { NotificationsScreen } from './presentation/screens/NotificationsScreen';
@@ -10,13 +10,8 @@ import type {
10
10
  import { NotificationDelivery } from '../../services/delivery/NotificationDelivery';
11
11
  import { ChannelManager } from '../../services/channels/ChannelManager';
12
12
  import { PreferencesManager } from '../../services/preferences/PreferencesManager';
13
+ import { devLog } from '../../utils/dev';
13
14
 
14
- /**
15
- * useNotificationActions - Offline Notification Actions
16
- *
17
- * All actions use AsyncStorage and expo-notifications.
18
- * NO backend - pure offline.
19
- */
20
15
  export const useNotificationActions = (state: any, setters: any) => {
21
16
  const {
22
17
  setNotifications,
@@ -35,7 +30,6 @@ export const useNotificationActions = (state: any, setters: any) => {
35
30
  try {
36
31
  setError(null);
37
32
 
38
- // Create notification
39
33
  const notification: Notification = {
40
34
  id: `notif_${Date.now()}`,
41
35
  title: options.title,
@@ -46,7 +40,6 @@ export const useNotificationActions = (state: any, setters: any) => {
46
40
  read: false,
47
41
  };
48
42
 
49
- // Save to AsyncStorage
50
43
  const data = await AsyncStorage.getItem('@notifications:list');
51
44
  const notifications: Notification[] = data ? JSON.parse(data) : [];
52
45
  notifications.unshift(notification);
@@ -55,9 +48,10 @@ export const useNotificationActions = (state: any, setters: any) => {
55
48
  JSON.stringify(notifications)
56
49
  );
57
50
 
58
- // Deliver using expo-notifications
59
51
  await notificationDelivery.deliver(notification);
60
52
 
53
+ devLog('[useNotificationActions] Notification sent:', notification.id);
54
+
61
55
  return [notification];
62
56
  } catch (err) {
63
57
  setError(
@@ -91,6 +85,8 @@ export const useNotificationActions = (state: any, setters: any) => {
91
85
  );
92
86
  setUnreadCount((prev: number) => Math.max(0, prev - 1));
93
87
 
88
+ devLog('[useNotificationActions] Marked as read:', notificationId);
89
+
94
90
  return true;
95
91
  } catch (err) {
96
92
  setError(
@@ -116,6 +112,8 @@ export const useNotificationActions = (state: any, setters: any) => {
116
112
  );
117
113
  setUnreadCount(0);
118
114
 
115
+ devLog('[useNotificationActions] All notifications marked as read');
116
+
119
117
  return true;
120
118
  } catch (err) {
121
119
  setError(
@@ -125,109 +123,9 @@ export const useNotificationActions = (state: any, setters: any) => {
125
123
  }
126
124
  }, [setNotifications, setUnreadCount, setError]);
127
125
 
128
- const deleteNotification = useCallback(
129
- async (notificationId: string): Promise<boolean> => {
130
- try {
131
- const data = await AsyncStorage.getItem('@notifications:list');
132
- const notifications: Notification[] = data ? JSON.parse(data) : [];
133
-
134
- const deleted = notifications.find((n) => n.id === notificationId);
135
- const filtered = notifications.filter((n) => n.id !== notificationId);
136
-
137
- await AsyncStorage.setItem(
138
- '@notifications:list',
139
- JSON.stringify(filtered)
140
- );
141
-
142
- setNotifications((prev: Notification[]) =>
143
- prev.filter((n) => n.id !== notificationId)
144
- );
145
-
146
- if (deleted && !deleted.read) {
147
- setUnreadCount((prev: number) => Math.max(0, prev - 1));
148
- }
149
-
150
- return true;
151
- } catch (err) {
152
- setError(
153
- err instanceof Error ? err.message : 'Failed to delete notification'
154
- );
155
- return false;
156
- }
157
- },
158
- [setNotifications, setUnreadCount, setError]
159
- );
160
-
161
- const registerChannel = useCallback(
162
- async (
163
- channelType: 'push' | 'in_app',
164
- preferences: Record<string, any> = {}
165
- ): Promise<NotificationChannel | null> => {
166
- try {
167
- const channel = await channelManager.register(channelType, preferences);
168
- if (channel) {
169
- setChannels((prev: NotificationChannel[]) => [...prev, channel]);
170
- }
171
- return channel;
172
- } catch (err) {
173
- setError(
174
- err instanceof Error ? err.message : 'Failed to register channel'
175
- );
176
- return null;
177
- }
178
- },
179
- [setChannels, setError]
180
- );
181
-
182
- const verifyChannel = useCallback(
183
- async (channelId: string): Promise<boolean> => {
184
- try {
185
- const success = await channelManager.verify(channelId);
186
- if (success) {
187
- setChannels((prev: NotificationChannel[]) =>
188
- prev.map((c) =>
189
- c.id === channelId ? { ...c, is_verified: true } : c
190
- )
191
- );
192
- }
193
- return success;
194
- } catch (err) {
195
- setError(
196
- err instanceof Error ? err.message : 'Failed to verify channel'
197
- );
198
- return false;
199
- }
200
- },
201
- [setChannels, setError]
202
- );
203
-
204
- const updatePreferences = useCallback(
205
- async (newPreferences: Partial<NotificationPreferences>): Promise<boolean> => {
206
- try {
207
- const success = await preferencesManager.update(newPreferences);
208
- if (success) {
209
- setPreferences((prev: NotificationPreferences | null) =>
210
- prev ? { ...prev, ...newPreferences } : null
211
- );
212
- }
213
- return success;
214
- } catch (err) {
215
- setError(
216
- err instanceof Error ? err.message : 'Failed to update preferences'
217
- );
218
- return false;
219
- }
220
- },
221
- [setPreferences, setError]
222
- );
223
-
224
126
  return {
225
127
  sendNotification,
226
128
  markAsRead,
227
129
  markAllAsRead,
228
- deleteNotification,
229
- registerChannel,
230
- verifyChannel,
231
- updatePreferences,
232
130
  };
233
- };
131
+ };