@umituz/react-native-notifications 1.0.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 (78) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +93 -0
  3. package/lib/index.d.ts +13 -0
  4. package/lib/index.d.ts.map +1 -0
  5. package/lib/index.js +15 -0
  6. package/lib/index.js.map +1 -0
  7. package/lib/infrastructure/config/notificationsConfig.d.ts +20 -0
  8. package/lib/infrastructure/config/notificationsConfig.d.ts.map +1 -0
  9. package/lib/infrastructure/config/notificationsConfig.js +81 -0
  10. package/lib/infrastructure/config/notificationsConfig.js.map +1 -0
  11. package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts +17 -0
  12. package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts.map +1 -0
  13. package/lib/infrastructure/hooks/actions/useNotificationActions.js +141 -0
  14. package/lib/infrastructure/hooks/actions/useNotificationActions.js.map +1 -0
  15. package/lib/infrastructure/hooks/state/useNotificationsState.d.ts +12 -0
  16. package/lib/infrastructure/hooks/state/useNotificationsState.d.ts.map +1 -0
  17. package/lib/infrastructure/hooks/state/useNotificationsState.js +30 -0
  18. package/lib/infrastructure/hooks/state/useNotificationsState.js.map +1 -0
  19. package/lib/infrastructure/hooks/types.d.ts +87 -0
  20. package/lib/infrastructure/hooks/types.d.ts.map +1 -0
  21. package/lib/infrastructure/hooks/types.js +8 -0
  22. package/lib/infrastructure/hooks/types.js.map +1 -0
  23. package/lib/infrastructure/hooks/useNotificationSettings.d.ts +10 -0
  24. package/lib/infrastructure/hooks/useNotificationSettings.d.ts.map +1 -0
  25. package/lib/infrastructure/hooks/useNotificationSettings.js +43 -0
  26. package/lib/infrastructure/hooks/useNotificationSettings.js.map +1 -0
  27. package/lib/infrastructure/hooks/useNotifications.d.ts +23 -0
  28. package/lib/infrastructure/hooks/useNotifications.d.ts.map +1 -0
  29. package/lib/infrastructure/hooks/useNotifications.js +51 -0
  30. package/lib/infrastructure/hooks/useNotifications.js.map +1 -0
  31. package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts +13 -0
  32. package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts.map +1 -0
  33. package/lib/infrastructure/hooks/utils/useNotificationRefresh.js +82 -0
  34. package/lib/infrastructure/hooks/utils/useNotificationRefresh.js.map +1 -0
  35. package/lib/infrastructure/services/NotificationManager.d.ts +138 -0
  36. package/lib/infrastructure/services/NotificationManager.d.ts.map +1 -0
  37. package/lib/infrastructure/services/NotificationManager.js +284 -0
  38. package/lib/infrastructure/services/NotificationManager.js.map +1 -0
  39. package/lib/infrastructure/services/NotificationService.d.ts +30 -0
  40. package/lib/infrastructure/services/NotificationService.d.ts.map +1 -0
  41. package/lib/infrastructure/services/NotificationService.js +41 -0
  42. package/lib/infrastructure/services/NotificationService.js.map +1 -0
  43. package/lib/infrastructure/services/channels/ChannelManager.d.ts +18 -0
  44. package/lib/infrastructure/services/channels/ChannelManager.d.ts.map +1 -0
  45. package/lib/infrastructure/services/channels/ChannelManager.js +87 -0
  46. package/lib/infrastructure/services/channels/ChannelManager.js.map +1 -0
  47. package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts +16 -0
  48. package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts.map +1 -0
  49. package/lib/infrastructure/services/delivery/NotificationDelivery.js +57 -0
  50. package/lib/infrastructure/services/delivery/NotificationDelivery.js.map +1 -0
  51. package/lib/infrastructure/services/preferences/PreferencesManager.d.ts +18 -0
  52. package/lib/infrastructure/services/preferences/PreferencesManager.d.ts.map +1 -0
  53. package/lib/infrastructure/services/preferences/PreferencesManager.js +65 -0
  54. package/lib/infrastructure/services/preferences/PreferencesManager.js.map +1 -0
  55. package/lib/infrastructure/services/types.d.ts +89 -0
  56. package/lib/infrastructure/services/types.d.ts.map +1 -0
  57. package/lib/infrastructure/services/types.js +7 -0
  58. package/lib/infrastructure/services/types.js.map +1 -0
  59. package/lib/infrastructure/storage/NotificationsStore.d.ts +23 -0
  60. package/lib/infrastructure/storage/NotificationsStore.d.ts.map +1 -0
  61. package/lib/infrastructure/storage/NotificationsStore.js +25 -0
  62. package/lib/infrastructure/storage/NotificationsStore.js.map +1 -0
  63. package/package.json +62 -0
  64. package/src/index.ts +34 -0
  65. package/src/infrastructure/config/notificationsConfig.ts +98 -0
  66. package/src/infrastructure/hooks/actions/useNotificationActions.ts +233 -0
  67. package/src/infrastructure/hooks/state/useNotificationsState.ts +46 -0
  68. package/src/infrastructure/hooks/types.ts +83 -0
  69. package/src/infrastructure/hooks/useNotificationSettings.ts +45 -0
  70. package/src/infrastructure/hooks/useNotifications.ts +70 -0
  71. package/src/infrastructure/hooks/utils/useNotificationRefresh.ts +107 -0
  72. package/src/infrastructure/services/NotificationManager.ts +326 -0
  73. package/src/infrastructure/services/NotificationService.ts +50 -0
  74. package/src/infrastructure/services/channels/ChannelManager.ts +111 -0
  75. package/src/infrastructure/services/delivery/NotificationDelivery.ts +65 -0
  76. package/src/infrastructure/services/preferences/PreferencesManager.ts +77 -0
  77. package/src/infrastructure/services/types.ts +81 -0
  78. package/src/infrastructure/storage/NotificationsStore.ts +39 -0
@@ -0,0 +1,43 @@
1
+ import { useState, useEffect } from 'react';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+ const STORAGE_KEY = 'notifications_enabled';
4
+ /**
5
+ * Simple notification settings hook
6
+ * Manages a single toggle for enabling/disabling notifications
7
+ */
8
+ export const useNotificationSettings = () => {
9
+ const [notificationsEnabled, setNotificationsEnabled] = useState(true);
10
+ const [isLoading, setIsLoading] = useState(true);
11
+ useEffect(() => {
12
+ const loadSettings = async () => {
13
+ try {
14
+ const value = await AsyncStorage.getItem(STORAGE_KEY);
15
+ if (value !== null) {
16
+ setNotificationsEnabled(value === 'true');
17
+ }
18
+ }
19
+ catch (error) {
20
+ // Silent failure - use default value
21
+ }
22
+ finally {
23
+ setIsLoading(false);
24
+ }
25
+ };
26
+ loadSettings();
27
+ }, []);
28
+ const toggleNotifications = async (value) => {
29
+ setNotificationsEnabled(value);
30
+ try {
31
+ await AsyncStorage.setItem(STORAGE_KEY, value.toString());
32
+ }
33
+ catch (error) {
34
+ // Silent failure
35
+ }
36
+ };
37
+ return {
38
+ notificationsEnabled,
39
+ setNotificationsEnabled: toggleNotifications,
40
+ isLoading,
41
+ };
42
+ };
43
+ //# sourceMappingURL=useNotificationSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNotificationSettings.js","sourceRoot":"","sources":["../../../src/infrastructure/hooks/useNotificationSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,YAAY,MAAM,2CAA2C,CAAC;AAErE,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAE5C;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,EAAE;IAC1C,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEjD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE;YAC9B,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;gBACtD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBACnB,uBAAuB,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;gBAC5C,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,qCAAqC;YACvC,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC,CAAC;QAEF,YAAY,EAAE,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,mBAAmB,GAAG,KAAK,EAAE,KAAc,EAAE,EAAE;QACnD,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,iBAAiB;QACnB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,oBAAoB;QACpB,uBAAuB,EAAE,mBAAmB;QAC5C,SAAS;KACV,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { UseNotificationsOptions } from './types';
2
+ export * from './types';
3
+ export declare function useNotifications(userId: string, options?: UseNotificationsOptions): {
4
+ loadMoreNotifications: () => Promise<void>;
5
+ refreshNotifications: () => Promise<void>;
6
+ refreshChannels: () => Promise<void>;
7
+ refreshPreferences: () => Promise<void>;
8
+ sendNotification: (options: import("./types").SendNotificationOptions) => Promise<import("./types").Notification[]>;
9
+ markAsRead: (notificationId: string) => Promise<boolean>;
10
+ markAllAsRead: () => Promise<boolean>;
11
+ deleteNotification: (notificationId: string) => Promise<boolean>;
12
+ registerChannel: (channelType: "push" | "in_app", preferences?: Record<string, any>) => Promise<import("./types").NotificationChannel | null>;
13
+ verifyChannel: (channelId: string) => Promise<boolean>;
14
+ updatePreferences: (newPreferences: Partial<import("./types").NotificationPreferences>) => Promise<boolean>;
15
+ notifications: import("./types").Notification[];
16
+ channels: import("./types").NotificationChannel[];
17
+ unreadCount: number;
18
+ preferences: import("./types").NotificationPreferences | null;
19
+ loading: boolean;
20
+ error: string | null;
21
+ hasMore: boolean;
22
+ };
23
+ //# sourceMappingURL=useNotifications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNotifications.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/hooks/useNotifications.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEvD,cAAc,SAAS,CAAC;AAExB,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B;;;;;;;;;;;;;;;;;;;EA6DrF"}
@@ -0,0 +1,51 @@
1
+ import { useEffect } from 'react';
2
+ import { useNotificationsState } from './state/useNotificationsState';
3
+ import { useNotificationActions } from './actions/useNotificationActions';
4
+ import { useNotificationRefresh } from './utils/useNotificationRefresh';
5
+ export * from './types';
6
+ export function useNotifications(userId, options = {}) {
7
+ const { autoRefresh = false, refreshInterval = 30000, pageSize = 20 } = options;
8
+ const { state, setNotifications, setChannels, setUnreadCount, setPreferences, setLoading, setError, setHasMore, } = useNotificationsState();
9
+ const setters = {
10
+ setNotifications,
11
+ setChannels,
12
+ setUnreadCount,
13
+ setPreferences,
14
+ setLoading,
15
+ setError,
16
+ setHasMore,
17
+ };
18
+ const actions = useNotificationActions(state, setters);
19
+ const refresh = useNotificationRefresh(pageSize, setters);
20
+ const loadMoreNotifications = () => refresh.loadMoreNotifications(state.notifications.length, state.hasMore, state.loading);
21
+ // Load initial data
22
+ useEffect(() => {
23
+ if (userId) {
24
+ refresh.refreshNotifications();
25
+ refresh.refreshChannels();
26
+ refresh.refreshPreferences();
27
+ }
28
+ else {
29
+ setNotifications([]);
30
+ setChannels([]);
31
+ setUnreadCount(0);
32
+ setPreferences(null);
33
+ }
34
+ }, [userId]);
35
+ // Auto-refresh setup
36
+ useEffect(() => {
37
+ if (!autoRefresh || !userId)
38
+ return;
39
+ const interval = setInterval(() => {
40
+ refresh.refreshNotifications();
41
+ }, refreshInterval);
42
+ return () => clearInterval(interval);
43
+ }, [autoRefresh, userId, refreshInterval]);
44
+ return {
45
+ ...state,
46
+ ...actions,
47
+ ...refresh,
48
+ loadMoreNotifications,
49
+ };
50
+ }
51
+ //# sourceMappingURL=useNotifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNotifications.js","sourceRoot":"","sources":["../../../src/infrastructure/hooks/useNotifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,cAAc,SAAS,CAAC;AAExB,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,UAAmC,EAAE;IACpF,MAAM,EAAE,WAAW,GAAG,KAAK,EAAE,eAAe,GAAG,KAAK,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAEhF,MAAM,EACJ,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,cAAc,EACd,UAAU,EACV,QAAQ,EACR,UAAU,GACX,GAAG,qBAAqB,EAAE,CAAC;IAE5B,MAAM,OAAO,GAAG;QACd,gBAAgB;QAChB,WAAW;QACX,cAAc;QACd,cAAc;QACd,UAAU;QACV,QAAQ;QACR,UAAU;KACX,CAAC;IAEF,MAAM,OAAO,GAAG,sBAAsB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE1D,MAAM,qBAAqB,GAAG,GAAG,EAAE,CACjC,OAAO,CAAC,qBAAqB,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAE1F,oBAAoB;IACpB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAC/B,OAAO,CAAC,eAAe,EAAE,CAAC;YAC1B,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,gBAAgB,CAAC,EAAE,CAAC,CAAC;YACrB,WAAW,CAAC,EAAE,CAAC,CAAC;YAChB,cAAc,CAAC,CAAC,CAAC,CAAC;YAClB,cAAc,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,qBAAqB;IACrB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,WAAW,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;YAChC,OAAO,CAAC,oBAAoB,EAAE,CAAC;QACjC,CAAC,EAAE,eAAe,CAAC,CAAC;QAEpB,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;IAE3C,OAAO;QACL,GAAG,KAAK;QACR,GAAG,OAAO;QACV,GAAG,OAAO;QACV,qBAAqB;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * useNotificationRefresh - Offline Notification Refresh
3
+ *
4
+ * Uses AsyncStorage for local data.
5
+ * NO backend - pure offline.
6
+ */
7
+ export declare const useNotificationRefresh: (pageSize: number, setters: any) => {
8
+ refreshNotifications: () => Promise<void>;
9
+ loadMoreNotifications: (currentLength: number, hasMore: boolean, loading: boolean) => Promise<void>;
10
+ refreshChannels: () => Promise<void>;
11
+ refreshPreferences: () => Promise<void>;
12
+ };
13
+ //# sourceMappingURL=useNotificationRefresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNotificationRefresh.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/hooks/utils/useNotificationRefresh.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,UAAU,MAAM,EAAE,SAAS,GAAG;;2CAwC3C,MAAM,WAAW,OAAO,WAAW,OAAO;;;CAsDnE,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { useCallback } from 'react';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+ import { ChannelManager } from '../../services/channels/ChannelManager';
4
+ import { PreferencesManager } from '../../services/preferences/PreferencesManager';
5
+ /**
6
+ * useNotificationRefresh - Offline Notification Refresh
7
+ *
8
+ * Uses AsyncStorage for local data.
9
+ * NO backend - pure offline.
10
+ */
11
+ export const useNotificationRefresh = (pageSize, setters) => {
12
+ const { setNotifications, setUnreadCount, setChannels, setPreferences, setLoading, setError, setHasMore, } = setters;
13
+ const channelManager = new ChannelManager();
14
+ const preferencesManager = new PreferencesManager();
15
+ const refreshNotifications = useCallback(async () => {
16
+ try {
17
+ setLoading(true);
18
+ setError(null);
19
+ // Load from AsyncStorage
20
+ const data = await AsyncStorage.getItem('@notifications:list');
21
+ const allNotifications = data ? JSON.parse(data) : [];
22
+ // Paginate
23
+ const paginated = allNotifications.slice(0, pageSize);
24
+ const unread = allNotifications.filter((n) => !n.read).length;
25
+ setNotifications(paginated);
26
+ setUnreadCount(unread);
27
+ setHasMore(allNotifications.length > pageSize);
28
+ }
29
+ catch (err) {
30
+ setError(err instanceof Error ? err.message : 'Failed to load notifications');
31
+ }
32
+ finally {
33
+ setLoading(false);
34
+ }
35
+ }, [pageSize, setNotifications, setUnreadCount, setHasMore, setLoading, setError]);
36
+ const loadMoreNotifications = useCallback(async (currentLength, hasMore, loading) => {
37
+ if (!hasMore || loading)
38
+ return;
39
+ try {
40
+ setLoading(true);
41
+ setError(null);
42
+ const data = await AsyncStorage.getItem('@notifications:list');
43
+ const allNotifications = data ? JSON.parse(data) : [];
44
+ const moreNotifications = allNotifications.slice(currentLength, currentLength + pageSize);
45
+ setNotifications((prev) => [...prev, ...moreNotifications]);
46
+ setHasMore(allNotifications.length > currentLength + pageSize);
47
+ }
48
+ catch (err) {
49
+ setError(err instanceof Error
50
+ ? err.message
51
+ : 'Failed to load more notifications');
52
+ }
53
+ finally {
54
+ setLoading(false);
55
+ }
56
+ }, [pageSize, setNotifications, setHasMore, setLoading, setError]);
57
+ const refreshChannels = useCallback(async () => {
58
+ try {
59
+ const channelsData = await channelManager.getActiveChannels();
60
+ setChannels(channelsData);
61
+ }
62
+ catch (err) {
63
+ // Silent failure
64
+ }
65
+ }, [setChannels]);
66
+ const refreshPreferences = useCallback(async () => {
67
+ try {
68
+ const prefsData = await preferencesManager.get();
69
+ setPreferences(prefsData);
70
+ }
71
+ catch (err) {
72
+ // Silent failure
73
+ }
74
+ }, [setPreferences]);
75
+ return {
76
+ refreshNotifications,
77
+ loadMoreNotifications,
78
+ refreshChannels,
79
+ refreshPreferences,
80
+ };
81
+ };
82
+ //# sourceMappingURL=useNotificationRefresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNotificationRefresh.js","sourceRoot":"","sources":["../../../../src/infrastructure/hooks/utils/useNotificationRefresh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * NotificationManager
3
+ *
4
+ * Offline-first notification system using expo-notifications.
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
+ */
23
+ /**
24
+ * Trigger types for notifications
25
+ */
26
+ export type NotificationTrigger = {
27
+ type: 'date';
28
+ date: Date;
29
+ } | {
30
+ type: 'daily';
31
+ hour: number;
32
+ minute: number;
33
+ } | {
34
+ type: 'weekly';
35
+ weekday: number;
36
+ hour: number;
37
+ minute: number;
38
+ } | {
39
+ type: 'monthly';
40
+ day: number;
41
+ hour: number;
42
+ minute: number;
43
+ };
44
+ /**
45
+ * Options for scheduling a notification
46
+ */
47
+ export interface ScheduleNotificationOptions {
48
+ title: string;
49
+ body: string;
50
+ data?: Record<string, any>;
51
+ trigger: NotificationTrigger;
52
+ sound?: boolean | string;
53
+ badge?: number;
54
+ categoryIdentifier?: string;
55
+ }
56
+ /**
57
+ * Scheduled notification details
58
+ */
59
+ export interface ScheduledNotification {
60
+ identifier: string;
61
+ content: {
62
+ title: string;
63
+ body: string;
64
+ data: Record<string, any>;
65
+ };
66
+ trigger: any;
67
+ }
68
+ /**
69
+ * NotificationManager
70
+ *
71
+ * Handles all notification operations using expo-notifications.
72
+ * Works completely offline - no backend, no user IDs, just device-local notifications.
73
+ */
74
+ export declare class NotificationManager {
75
+ /**
76
+ * Configure notification handler (how notifications appear when app is in foreground)
77
+ */
78
+ static configure(): void;
79
+ /**
80
+ * Request notification permissions
81
+ * iOS: Shows system permission dialog
82
+ * Android: Permissions granted by default (Android 13+ requires runtime permission)
83
+ */
84
+ requestPermissions(): Promise<boolean>;
85
+ /**
86
+ * Check if notification permissions are granted
87
+ */
88
+ hasPermissions(): Promise<boolean>;
89
+ /**
90
+ * Create Android notification channels (required for Android 8+)
91
+ */
92
+ private createAndroidChannels;
93
+ /**
94
+ * Schedule a notification
95
+ *
96
+ * @example
97
+ * // Specific date
98
+ * const id = await manager.scheduleNotification({
99
+ * title: 'Bill Reminder',
100
+ * body: 'Electricity bill due today',
101
+ * trigger: { type: 'date', date: new Date('2025-01-15T09:00:00') }
102
+ * });
103
+ *
104
+ * @example
105
+ * // Daily reminder
106
+ * const id = await manager.scheduleNotification({
107
+ * title: 'Daily Workout',
108
+ * body: 'Time for your morning workout!',
109
+ * trigger: { type: 'daily', hour: 7, minute: 0 }
110
+ * });
111
+ */
112
+ scheduleNotification(options: ScheduleNotificationOptions): Promise<string>;
113
+ /**
114
+ * Cancel a scheduled notification
115
+ */
116
+ cancelNotification(notificationId: string): Promise<void>;
117
+ /**
118
+ * Cancel all scheduled notifications
119
+ */
120
+ cancelAllNotifications(): Promise<void>;
121
+ /**
122
+ * Get all scheduled notifications
123
+ */
124
+ getScheduledNotifications(): Promise<ScheduledNotification[]>;
125
+ /**
126
+ * Dismiss all delivered notifications (clear from notification center)
127
+ */
128
+ dismissAllNotifications(): Promise<void>;
129
+ /**
130
+ * Get notification badge count (iOS only)
131
+ */
132
+ getBadgeCount(): Promise<number>;
133
+ /**
134
+ * Set notification badge count (iOS only)
135
+ */
136
+ setBadgeCount(count: number): Promise<void>;
137
+ }
138
+ //# sourceMappingURL=NotificationManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationManager.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/services/NotificationManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC3B,CAAC;IACF,OAAO,EAAE,GAAG,CAAC;CACd;AAED;;;;;GAKG;AACH,qBAAa,mBAAmB;IAC9B;;OAEG;IACH,MAAM,CAAC,SAAS;IAUhB;;;;OAIG;IACG,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;IA0B5C;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAWxC;;OAEG;YACW,qBAAqB;IAkCnC;;;;;;;;;;;;;;;;;;OAkBG;IACG,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,MAAM,CAAC;IAyDjF;;OAEG;IACG,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/D;;OAEG;IACG,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAS7C;;OAEG;IACG,yBAAyB,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAkBnE;;OAEG;IACG,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9C;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAYtC;;OAEG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAUlD"}
@@ -0,0 +1,284 @@
1
+ /**
2
+ * NotificationManager
3
+ *
4
+ * Offline-first notification system using expo-notifications.
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
+ */
23
+ import * as Notifications from 'expo-notifications';
24
+ import * as Device from 'expo-device';
25
+ import { Platform } from 'react-native';
26
+ /**
27
+ * NotificationManager
28
+ *
29
+ * Handles all notification operations using expo-notifications.
30
+ * Works completely offline - no backend, no user IDs, just device-local notifications.
31
+ */
32
+ export class NotificationManager {
33
+ /**
34
+ * Configure notification handler (how notifications appear when app is in foreground)
35
+ */
36
+ static configure() {
37
+ Notifications.setNotificationHandler({
38
+ handleNotification: async () => ({
39
+ shouldShowAlert: true,
40
+ shouldPlaySound: true,
41
+ shouldSetBadge: true,
42
+ }),
43
+ });
44
+ }
45
+ /**
46
+ * Request notification permissions
47
+ * iOS: Shows system permission dialog
48
+ * Android: Permissions granted by default (Android 13+ requires runtime permission)
49
+ */
50
+ async requestPermissions() {
51
+ try {
52
+ if (!Device.isDevice) {
53
+ console.warn('[NotificationManager] Notifications only work on physical devices');
54
+ return false;
55
+ }
56
+ const { status: existingStatus } = await Notifications.getPermissionsAsync();
57
+ let finalStatus = existingStatus;
58
+ if (existingStatus !== 'granted') {
59
+ const { status } = await Notifications.requestPermissionsAsync();
60
+ finalStatus = status;
61
+ }
62
+ if (Platform.OS === 'android') {
63
+ await this.createAndroidChannels();
64
+ }
65
+ return finalStatus === 'granted';
66
+ }
67
+ catch (error) {
68
+ console.error('[NotificationManager] Permission request failed:', error);
69
+ return false;
70
+ }
71
+ }
72
+ /**
73
+ * Check if notification permissions are granted
74
+ */
75
+ async hasPermissions() {
76
+ try {
77
+ if (!Device.isDevice)
78
+ return false;
79
+ const { status } = await Notifications.getPermissionsAsync();
80
+ return status === 'granted';
81
+ }
82
+ catch (error) {
83
+ console.error('[NotificationManager] Permission check failed:', error);
84
+ return false;
85
+ }
86
+ }
87
+ /**
88
+ * Create Android notification channels (required for Android 8+)
89
+ */
90
+ async createAndroidChannels() {
91
+ if (Platform.OS !== 'android')
92
+ return;
93
+ try {
94
+ await Notifications.setNotificationChannelAsync('default', {
95
+ name: 'Default',
96
+ importance: Notifications.AndroidImportance.DEFAULT,
97
+ vibrationPattern: [0, 250, 250, 250],
98
+ sound: 'default',
99
+ lightColor: '#3B82F6',
100
+ });
101
+ await Notifications.setNotificationChannelAsync('reminders', {
102
+ name: 'Reminders',
103
+ importance: Notifications.AndroidImportance.HIGH,
104
+ vibrationPattern: [0, 250, 250, 250],
105
+ sound: 'default',
106
+ lightColor: '#3B82F6',
107
+ enableVibrate: true,
108
+ });
109
+ await Notifications.setNotificationChannelAsync('urgent', {
110
+ name: 'Urgent',
111
+ importance: Notifications.AndroidImportance.MAX,
112
+ vibrationPattern: [0, 500, 250, 500],
113
+ sound: 'default',
114
+ lightColor: '#EF4444',
115
+ enableVibrate: true,
116
+ });
117
+ }
118
+ catch (error) {
119
+ console.error('[NotificationManager] Android channel creation failed:', error);
120
+ }
121
+ }
122
+ /**
123
+ * Schedule a notification
124
+ *
125
+ * @example
126
+ * // Specific date
127
+ * const id = await manager.scheduleNotification({
128
+ * title: 'Bill Reminder',
129
+ * body: 'Electricity bill due today',
130
+ * trigger: { type: 'date', date: new Date('2025-01-15T09:00:00') }
131
+ * });
132
+ *
133
+ * @example
134
+ * // Daily reminder
135
+ * const id = await manager.scheduleNotification({
136
+ * title: 'Daily Workout',
137
+ * body: 'Time for your morning workout!',
138
+ * trigger: { type: 'daily', hour: 7, minute: 0 }
139
+ * });
140
+ */
141
+ async scheduleNotification(options) {
142
+ try {
143
+ const { title, body, data = {}, trigger, sound = true, badge, categoryIdentifier } = options;
144
+ let notificationTrigger;
145
+ if (trigger.type === 'date') {
146
+ notificationTrigger = {
147
+ date: trigger.date,
148
+ channelId: categoryIdentifier || 'default',
149
+ };
150
+ }
151
+ else if (trigger.type === 'daily') {
152
+ notificationTrigger = {
153
+ hour: trigger.hour,
154
+ minute: trigger.minute,
155
+ repeats: true,
156
+ channelId: categoryIdentifier || 'reminders',
157
+ };
158
+ }
159
+ else if (trigger.type === 'weekly') {
160
+ notificationTrigger = {
161
+ weekday: trigger.weekday,
162
+ hour: trigger.hour,
163
+ minute: trigger.minute,
164
+ repeats: true,
165
+ channelId: categoryIdentifier || 'reminders',
166
+ };
167
+ }
168
+ else if (trigger.type === 'monthly') {
169
+ notificationTrigger = {
170
+ day: trigger.day,
171
+ hour: trigger.hour,
172
+ minute: trigger.minute,
173
+ repeats: true,
174
+ channelId: categoryIdentifier || 'reminders',
175
+ };
176
+ }
177
+ const notificationId = await Notifications.scheduleNotificationAsync({
178
+ content: {
179
+ title,
180
+ body,
181
+ data,
182
+ sound: sound === true ? 'default' : sound || undefined,
183
+ badge,
184
+ categoryIdentifier,
185
+ priority: Notifications.AndroidNotificationPriority.HIGH,
186
+ vibrate: [0, 250, 250, 250],
187
+ },
188
+ trigger: notificationTrigger,
189
+ });
190
+ return notificationId;
191
+ }
192
+ catch (error) {
193
+ console.error('[NotificationManager] Schedule notification failed:', error);
194
+ throw error;
195
+ }
196
+ }
197
+ /**
198
+ * Cancel a scheduled notification
199
+ */
200
+ async cancelNotification(notificationId) {
201
+ try {
202
+ await Notifications.cancelScheduledNotificationAsync(notificationId);
203
+ }
204
+ catch (error) {
205
+ console.error('[NotificationManager] Cancel notification failed:', error);
206
+ throw error;
207
+ }
208
+ }
209
+ /**
210
+ * Cancel all scheduled notifications
211
+ */
212
+ async cancelAllNotifications() {
213
+ try {
214
+ await Notifications.cancelAllScheduledNotificationsAsync();
215
+ }
216
+ catch (error) {
217
+ console.error('[NotificationManager] Cancel all notifications failed:', error);
218
+ throw error;
219
+ }
220
+ }
221
+ /**
222
+ * Get all scheduled notifications
223
+ */
224
+ async getScheduledNotifications() {
225
+ try {
226
+ const notifications = await Notifications.getAllScheduledNotificationsAsync();
227
+ return notifications.map(notification => ({
228
+ identifier: notification.identifier,
229
+ content: {
230
+ title: notification.content.title || '',
231
+ body: notification.content.body || '',
232
+ data: notification.content.data,
233
+ },
234
+ trigger: notification.trigger,
235
+ }));
236
+ }
237
+ catch (error) {
238
+ console.error('[NotificationManager] Get scheduled notifications failed:', error);
239
+ return [];
240
+ }
241
+ }
242
+ /**
243
+ * Dismiss all delivered notifications (clear from notification center)
244
+ */
245
+ async dismissAllNotifications() {
246
+ try {
247
+ await Notifications.dismissAllNotificationsAsync();
248
+ }
249
+ catch (error) {
250
+ console.error('[NotificationManager] Dismiss all notifications failed:', error);
251
+ throw error;
252
+ }
253
+ }
254
+ /**
255
+ * Get notification badge count (iOS only)
256
+ */
257
+ async getBadgeCount() {
258
+ try {
259
+ if (Platform.OS === 'ios') {
260
+ return await Notifications.getBadgeCountAsync();
261
+ }
262
+ return 0;
263
+ }
264
+ catch (error) {
265
+ console.error('[NotificationManager] Get badge count failed:', error);
266
+ return 0;
267
+ }
268
+ }
269
+ /**
270
+ * Set notification badge count (iOS only)
271
+ */
272
+ async setBadgeCount(count) {
273
+ try {
274
+ if (Platform.OS === 'ios') {
275
+ await Notifications.setBadgeCountAsync(count);
276
+ }
277
+ }
278
+ catch (error) {
279
+ console.error('[NotificationManager] Set badge count failed:', error);
280
+ throw error;
281
+ }
282
+ }
283
+ }
284
+ //# sourceMappingURL=NotificationManager.js.map