@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.
- package/LICENSE +22 -0
- package/README.md +93 -0
- package/lib/index.d.ts +13 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +15 -0
- package/lib/index.js.map +1 -0
- package/lib/infrastructure/config/notificationsConfig.d.ts +20 -0
- package/lib/infrastructure/config/notificationsConfig.d.ts.map +1 -0
- package/lib/infrastructure/config/notificationsConfig.js +81 -0
- package/lib/infrastructure/config/notificationsConfig.js.map +1 -0
- package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts +17 -0
- package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts.map +1 -0
- package/lib/infrastructure/hooks/actions/useNotificationActions.js +141 -0
- package/lib/infrastructure/hooks/actions/useNotificationActions.js.map +1 -0
- package/lib/infrastructure/hooks/state/useNotificationsState.d.ts +12 -0
- package/lib/infrastructure/hooks/state/useNotificationsState.d.ts.map +1 -0
- package/lib/infrastructure/hooks/state/useNotificationsState.js +30 -0
- package/lib/infrastructure/hooks/state/useNotificationsState.js.map +1 -0
- package/lib/infrastructure/hooks/types.d.ts +87 -0
- package/lib/infrastructure/hooks/types.d.ts.map +1 -0
- package/lib/infrastructure/hooks/types.js +8 -0
- package/lib/infrastructure/hooks/types.js.map +1 -0
- package/lib/infrastructure/hooks/useNotificationSettings.d.ts +10 -0
- package/lib/infrastructure/hooks/useNotificationSettings.d.ts.map +1 -0
- package/lib/infrastructure/hooks/useNotificationSettings.js +43 -0
- package/lib/infrastructure/hooks/useNotificationSettings.js.map +1 -0
- package/lib/infrastructure/hooks/useNotifications.d.ts +23 -0
- package/lib/infrastructure/hooks/useNotifications.d.ts.map +1 -0
- package/lib/infrastructure/hooks/useNotifications.js +51 -0
- package/lib/infrastructure/hooks/useNotifications.js.map +1 -0
- package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts +13 -0
- package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts.map +1 -0
- package/lib/infrastructure/hooks/utils/useNotificationRefresh.js +82 -0
- package/lib/infrastructure/hooks/utils/useNotificationRefresh.js.map +1 -0
- package/lib/infrastructure/services/NotificationManager.d.ts +138 -0
- package/lib/infrastructure/services/NotificationManager.d.ts.map +1 -0
- package/lib/infrastructure/services/NotificationManager.js +284 -0
- package/lib/infrastructure/services/NotificationManager.js.map +1 -0
- package/lib/infrastructure/services/NotificationService.d.ts +30 -0
- package/lib/infrastructure/services/NotificationService.d.ts.map +1 -0
- package/lib/infrastructure/services/NotificationService.js +41 -0
- package/lib/infrastructure/services/NotificationService.js.map +1 -0
- package/lib/infrastructure/services/channels/ChannelManager.d.ts +18 -0
- package/lib/infrastructure/services/channels/ChannelManager.d.ts.map +1 -0
- package/lib/infrastructure/services/channels/ChannelManager.js +87 -0
- package/lib/infrastructure/services/channels/ChannelManager.js.map +1 -0
- package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts +16 -0
- package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts.map +1 -0
- package/lib/infrastructure/services/delivery/NotificationDelivery.js +57 -0
- package/lib/infrastructure/services/delivery/NotificationDelivery.js.map +1 -0
- package/lib/infrastructure/services/preferences/PreferencesManager.d.ts +18 -0
- package/lib/infrastructure/services/preferences/PreferencesManager.d.ts.map +1 -0
- package/lib/infrastructure/services/preferences/PreferencesManager.js +65 -0
- package/lib/infrastructure/services/preferences/PreferencesManager.js.map +1 -0
- package/lib/infrastructure/services/types.d.ts +89 -0
- package/lib/infrastructure/services/types.d.ts.map +1 -0
- package/lib/infrastructure/services/types.js +7 -0
- package/lib/infrastructure/services/types.js.map +1 -0
- package/lib/infrastructure/storage/NotificationsStore.d.ts +23 -0
- package/lib/infrastructure/storage/NotificationsStore.d.ts.map +1 -0
- package/lib/infrastructure/storage/NotificationsStore.js +25 -0
- package/lib/infrastructure/storage/NotificationsStore.js.map +1 -0
- package/package.json +62 -0
- package/src/index.ts +34 -0
- package/src/infrastructure/config/notificationsConfig.ts +98 -0
- package/src/infrastructure/hooks/actions/useNotificationActions.ts +233 -0
- package/src/infrastructure/hooks/state/useNotificationsState.ts +46 -0
- package/src/infrastructure/hooks/types.ts +83 -0
- package/src/infrastructure/hooks/useNotificationSettings.ts +45 -0
- package/src/infrastructure/hooks/useNotifications.ts +70 -0
- package/src/infrastructure/hooks/utils/useNotificationRefresh.ts +107 -0
- package/src/infrastructure/services/NotificationManager.ts +326 -0
- package/src/infrastructure/services/NotificationService.ts +50 -0
- package/src/infrastructure/services/channels/ChannelManager.ts +111 -0
- package/src/infrastructure/services/delivery/NotificationDelivery.ts +65 -0
- package/src/infrastructure/services/preferences/PreferencesManager.ts +77 -0
- package/src/infrastructure/services/types.ts +81 -0
- 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
|