@umituz/react-native-notifications 1.1.6 → 1.3.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/package.json +18 -40
- package/src/index.ts +84 -13
- package/src/infrastructure/config/reminderPresets.ts +120 -0
- package/src/infrastructure/hooks/useQuietHoursActions.ts +52 -0
- package/src/infrastructure/hooks/useReminderActions.ts +147 -0
- package/src/infrastructure/services/types.ts +137 -44
- package/src/infrastructure/storage/RemindersStore.ts +150 -0
- package/src/presentation/components/FormButton.tsx +66 -0
- package/src/presentation/components/FrequencySelector.tsx +72 -0
- package/src/presentation/components/NotificationsSection.tsx +96 -159
- package/src/presentation/components/QuietHoursCard.tsx +105 -0
- package/src/presentation/components/ReminderForm.tsx +165 -0
- package/src/presentation/components/ReminderItem.tsx +124 -0
- package/src/presentation/components/TimePresetSelector.tsx +100 -0
- package/src/presentation/components/WeekdaySelector.tsx +61 -0
- package/src/presentation/screens/NotificationSettingsScreen.tsx +210 -0
- package/src/presentation/screens/ReminderListScreen.tsx +138 -0
- package/src/types/global.d.ts +11 -8
- package/lib/index.d.ts +0 -16
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -23
- package/lib/index.js.map +0 -1
- package/lib/infrastructure/config/notificationsConfig.d.ts +0 -20
- package/lib/infrastructure/config/notificationsConfig.d.ts.map +0 -1
- package/lib/infrastructure/config/notificationsConfig.js +0 -81
- package/lib/infrastructure/config/notificationsConfig.js.map +0 -1
- package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts +0 -7
- package/lib/infrastructure/hooks/actions/useNotificationActions.d.ts.map +0 -1
- package/lib/infrastructure/hooks/actions/useNotificationActions.js +0 -75
- package/lib/infrastructure/hooks/actions/useNotificationActions.js.map +0 -1
- package/lib/infrastructure/hooks/actions/useNotificationManagementActions.d.ts +0 -8
- package/lib/infrastructure/hooks/actions/useNotificationManagementActions.d.ts.map +0 -1
- package/lib/infrastructure/hooks/actions/useNotificationManagementActions.js +0 -78
- package/lib/infrastructure/hooks/actions/useNotificationManagementActions.js.map +0 -1
- package/lib/infrastructure/hooks/state/useNotificationsState.d.ts +0 -12
- package/lib/infrastructure/hooks/state/useNotificationsState.d.ts.map +0 -1
- package/lib/infrastructure/hooks/state/useNotificationsState.js +0 -30
- package/lib/infrastructure/hooks/state/useNotificationsState.js.map +0 -1
- package/lib/infrastructure/hooks/types.d.ts +0 -87
- package/lib/infrastructure/hooks/types.d.ts.map +0 -1
- package/lib/infrastructure/hooks/types.js +0 -8
- package/lib/infrastructure/hooks/types.js.map +0 -1
- package/lib/infrastructure/hooks/useNotificationSettings.d.ts +0 -10
- package/lib/infrastructure/hooks/useNotificationSettings.d.ts.map +0 -1
- package/lib/infrastructure/hooks/useNotificationSettings.js +0 -43
- package/lib/infrastructure/hooks/useNotificationSettings.js.map +0 -1
- package/lib/infrastructure/hooks/useNotifications.d.ts +0 -24
- package/lib/infrastructure/hooks/useNotifications.d.ts.map +0 -1
- package/lib/infrastructure/hooks/useNotifications.js +0 -72
- package/lib/infrastructure/hooks/useNotifications.js.map +0 -1
- package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts +0 -8
- package/lib/infrastructure/hooks/utils/useNotificationRefresh.d.ts.map +0 -1
- package/lib/infrastructure/hooks/utils/useNotificationRefresh.js +0 -99
- package/lib/infrastructure/hooks/utils/useNotificationRefresh.js.map +0 -1
- package/lib/infrastructure/services/NotificationBadgeManager.d.ts +0 -5
- package/lib/infrastructure/services/NotificationBadgeManager.d.ts.map +0 -1
- package/lib/infrastructure/services/NotificationBadgeManager.js +0 -29
- package/lib/infrastructure/services/NotificationBadgeManager.js.map +0 -1
- package/lib/infrastructure/services/NotificationManager.d.ts +0 -59
- package/lib/infrastructure/services/NotificationManager.d.ts.map +0 -1
- package/lib/infrastructure/services/NotificationManager.js +0 -118
- package/lib/infrastructure/services/NotificationManager.js.map +0 -1
- package/lib/infrastructure/services/NotificationPermissions.d.ts +0 -6
- package/lib/infrastructure/services/NotificationPermissions.d.ts.map +0 -1
- package/lib/infrastructure/services/NotificationPermissions.js +0 -75
- package/lib/infrastructure/services/NotificationPermissions.js.map +0 -1
- package/lib/infrastructure/services/NotificationScheduler.d.ts +0 -8
- package/lib/infrastructure/services/NotificationScheduler.d.ts.map +0 -1
- package/lib/infrastructure/services/NotificationScheduler.js +0 -72
- package/lib/infrastructure/services/NotificationScheduler.js.map +0 -1
- package/lib/infrastructure/services/NotificationService.d.ts +0 -30
- package/lib/infrastructure/services/NotificationService.d.ts.map +0 -1
- package/lib/infrastructure/services/NotificationService.js +0 -41
- package/lib/infrastructure/services/NotificationService.js.map +0 -1
- package/lib/infrastructure/services/channels/ChannelManager.d.ts +0 -18
- package/lib/infrastructure/services/channels/ChannelManager.d.ts.map +0 -1
- package/lib/infrastructure/services/channels/ChannelManager.js +0 -87
- package/lib/infrastructure/services/channels/ChannelManager.js.map +0 -1
- package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts +0 -10
- package/lib/infrastructure/services/delivery/NotificationDelivery.d.ts.map +0 -1
- package/lib/infrastructure/services/delivery/NotificationDelivery.js +0 -71
- package/lib/infrastructure/services/delivery/NotificationDelivery.js.map +0 -1
- package/lib/infrastructure/services/preferences/PreferencesManager.d.ts +0 -18
- package/lib/infrastructure/services/preferences/PreferencesManager.d.ts.map +0 -1
- package/lib/infrastructure/services/preferences/PreferencesManager.js +0 -65
- package/lib/infrastructure/services/preferences/PreferencesManager.js.map +0 -1
- package/lib/infrastructure/services/types.d.ts +0 -89
- package/lib/infrastructure/services/types.d.ts.map +0 -1
- package/lib/infrastructure/services/types.js +0 -7
- package/lib/infrastructure/services/types.js.map +0 -1
- package/lib/infrastructure/storage/NotificationsStore.d.ts +0 -23
- package/lib/infrastructure/storage/NotificationsStore.d.ts.map +0 -1
- package/lib/infrastructure/storage/NotificationsStore.js +0 -26
- package/lib/infrastructure/storage/NotificationsStore.js.map +0 -1
- package/lib/infrastructure/utils/dev.d.ts +0 -5
- package/lib/infrastructure/utils/dev.d.ts.map +0 -1
- package/lib/infrastructure/utils/dev.js +0 -24
- package/lib/infrastructure/utils/dev.js.map +0 -1
- package/lib/presentation/components/NotificationsSection.d.ts +0 -17
- package/lib/presentation/components/NotificationsSection.d.ts.map +0 -1
- package/lib/presentation/components/NotificationsSection.js +0 -132
- package/lib/presentation/components/NotificationsSection.js.map +0 -1
- package/lib/presentation/screens/NotificationsScreen.d.ts +0 -20
- package/lib/presentation/screens/NotificationsScreen.d.ts.map +0 -1
- package/lib/presentation/screens/NotificationsScreen.js +0 -74
- package/lib/presentation/screens/NotificationsScreen.js.map +0 -1
- package/src/__tests__/NotificationManager.test.ts +0 -215
- package/src/__tests__/useNotificationActions.test.ts +0 -189
- package/src/__tests__/useNotificationRefresh.test.ts +0 -213
- package/src/infrastructure/hooks/actions/useNotificationActions.ts +0 -131
- package/src/infrastructure/hooks/actions/useNotificationManagementActions.ts +0 -131
- package/src/infrastructure/hooks/state/useNotificationsState.ts +0 -46
- package/src/infrastructure/hooks/types.ts +0 -83
- package/src/infrastructure/hooks/useNotifications.ts +0 -96
- package/src/infrastructure/hooks/utils/useNotificationRefresh.ts +0 -131
- package/src/infrastructure/services/channels/ChannelManager.ts +0 -111
- package/src/infrastructure/services/delivery/NotificationDelivery.ts +0 -83
- package/src/infrastructure/services/preferences/PreferencesManager.ts +0 -77
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notifications Screen - Dynamic and Reusable
|
|
3
|
-
*
|
|
4
|
-
* A clean notification settings screen that accepts all text and configuration
|
|
5
|
-
* as props to make it completely reusable across different applications.
|
|
6
|
-
*/
|
|
7
|
-
import React from 'react';
|
|
8
|
-
export interface NotificationsScreenProps {
|
|
9
|
-
translations: {
|
|
10
|
-
title: string;
|
|
11
|
-
description: string;
|
|
12
|
-
loadingText?: string;
|
|
13
|
-
};
|
|
14
|
-
iconName?: string;
|
|
15
|
-
iconColor?: string;
|
|
16
|
-
testID?: string;
|
|
17
|
-
}
|
|
18
|
-
export declare const NotificationsScreen: React.FC<NotificationsScreenProps>;
|
|
19
|
-
export default NotificationsScreen;
|
|
20
|
-
//# sourceMappingURL=NotificationsScreen.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationsScreen.d.ts","sourceRoot":"","sources":["../../../src/presentation/screens/NotificationsScreen.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAkB,MAAM,OAAO,CAAC;AAOvC,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAkDlE,CAAC;AA+BF,eAAe,mBAAmB,CAAC"}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notifications Screen - Dynamic and Reusable
|
|
3
|
-
*
|
|
4
|
-
* A clean notification settings screen that accepts all text and configuration
|
|
5
|
-
* as props to make it completely reusable across different applications.
|
|
6
|
-
*/
|
|
7
|
-
import React, { useMemo } from 'react';
|
|
8
|
-
import { View, StyleSheet, ActivityIndicator } from 'react-native';
|
|
9
|
-
import { AtomicIcon, AtomicSwitch, AtomicCard, AtomicText, ScreenLayout, STATIC_TOKENS } from '@umituz/react-native-design-system';
|
|
10
|
-
import { useAppDesignTokens } from '@umituz/react-native-design-system-theme';
|
|
11
|
-
import { useNotificationSettings } from '../../infrastructure/hooks/useNotificationSettings';
|
|
12
|
-
export const NotificationsScreen = ({ translations, iconName = 'bell', iconColor = 'primary', testID = 'notifications-screen', }) => {
|
|
13
|
-
const tokens = useAppDesignTokens();
|
|
14
|
-
const styles = useMemo(() => getStyles(tokens), [tokens]);
|
|
15
|
-
const { notificationsEnabled, setNotificationsEnabled, isLoading } = useNotificationSettings();
|
|
16
|
-
if (isLoading) {
|
|
17
|
-
return (<ScreenLayout testID={testID}>
|
|
18
|
-
<View style={styles.loadingContainer}>
|
|
19
|
-
<ActivityIndicator size="large" color={tokens.colors.primary}/>
|
|
20
|
-
<AtomicText type="bodyMedium" style={{ color: tokens.colors.textSecondary, marginTop: STATIC_TOKENS.spacing.md }}>
|
|
21
|
-
{translations.loadingText || 'Loading...'}
|
|
22
|
-
</AtomicText>
|
|
23
|
-
</View>
|
|
24
|
-
</ScreenLayout>);
|
|
25
|
-
}
|
|
26
|
-
return (<ScreenLayout testID={testID} hideScrollIndicator>
|
|
27
|
-
<AtomicCard style={styles.card}>
|
|
28
|
-
<View style={styles.settingItem}>
|
|
29
|
-
<View style={styles.iconContainer}>
|
|
30
|
-
<AtomicIcon name={iconName} size="lg" color={iconColor}/>
|
|
31
|
-
</View>
|
|
32
|
-
<View style={styles.textContainer}>
|
|
33
|
-
<AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
|
|
34
|
-
{translations.title}
|
|
35
|
-
</AtomicText>
|
|
36
|
-
<AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary, marginTop: STATIC_TOKENS.spacing.xs }}>
|
|
37
|
-
{translations.description}
|
|
38
|
-
</AtomicText>
|
|
39
|
-
</View>
|
|
40
|
-
<AtomicSwitch value={notificationsEnabled} onValueChange={setNotificationsEnabled} testID="notifications-toggle"/>
|
|
41
|
-
</View>
|
|
42
|
-
</AtomicCard>
|
|
43
|
-
</ScreenLayout>);
|
|
44
|
-
};
|
|
45
|
-
const getStyles = (tokens) => StyleSheet.create({
|
|
46
|
-
loadingContainer: {
|
|
47
|
-
flex: 1,
|
|
48
|
-
justifyContent: 'center',
|
|
49
|
-
alignItems: 'center',
|
|
50
|
-
},
|
|
51
|
-
card: {
|
|
52
|
-
padding: STATIC_TOKENS.spacing.lg,
|
|
53
|
-
backgroundColor: tokens.colors.surface,
|
|
54
|
-
},
|
|
55
|
-
settingItem: {
|
|
56
|
-
flexDirection: 'row',
|
|
57
|
-
alignItems: 'center',
|
|
58
|
-
},
|
|
59
|
-
iconContainer: {
|
|
60
|
-
width: 48,
|
|
61
|
-
height: 48,
|
|
62
|
-
borderRadius: 24,
|
|
63
|
-
backgroundColor: tokens.colors.surfaceSecondary,
|
|
64
|
-
justifyContent: 'center',
|
|
65
|
-
alignItems: 'center',
|
|
66
|
-
marginRight: STATIC_TOKENS.spacing.md,
|
|
67
|
-
},
|
|
68
|
-
textContainer: {
|
|
69
|
-
flex: 1,
|
|
70
|
-
marginRight: STATIC_TOKENS.spacing.md,
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
export default NotificationsScreen;
|
|
74
|
-
//# sourceMappingURL=NotificationsScreen.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationsScreen.js","sourceRoot":"","sources":["../../../src/presentation/screens/NotificationsScreen.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnI,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oDAAoD,CAAC;AAc7F,MAAM,CAAC,MAAM,mBAAmB,GAAuC,CAAC,EACtE,YAAY,EACZ,QAAQ,GAAG,MAAM,EACjB,SAAS,GAAG,SAAS,EACrB,MAAM,GAAG,sBAAsB,GAChC,EAAE,EAAE;IACH,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,MAAM,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,SAAS,EAAE,GAAG,uBAAuB,EAAE,CAAC;IAE/F,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CACL,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAC3B;QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CACnC;UAAA,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAC7D;UAAA,CAAC,UAAU,CACT,IAAI,CAAC,YAAY,CACjB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAEnF;YAAA,CAAC,YAAY,CAAC,WAAW,IAAI,YAAY,CAC3C;UAAA,EAAE,UAAU,CACd;QAAA,EAAE,IAAI,CACR;MAAA,EAAE,YAAY,CAAC,CAChB,CAAC;IACJ,CAAC;IAED,OAAO,CACL,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAC/C;MAAA,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAC7B;QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAC9B;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAChC;YAAA,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,EACzD;UAAA,EAAE,IAAI,CACN;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAChC;YAAA,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CACvE;cAAA,CAAC,YAAY,CAAC,KAAK,CACrB;YAAA,EAAE,UAAU,CACZ;YAAA,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAC9G;cAAA,CAAC,YAAY,CAAC,WAAW,CAC3B;YAAA,EAAE,UAAU,CACd;UAAA,EAAE,IAAI,CACN;UAAA,CAAC,YAAY,CACX,KAAK,CAAC,CAAC,oBAAoB,CAAC,CAC5B,aAAa,CAAC,CAAC,uBAAuB,CAAC,CACvC,MAAM,CAAC,sBAAsB,EAEjC;QAAA,EAAE,IAAI,CACR;MAAA,EAAE,UAAU,CACd;IAAA,EAAE,YAAY,CAAC,CAChB,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,MAAoB,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;IAC5D,gBAAgB,EAAE;QAChB,IAAI,EAAE,CAAC;QACP,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;KACrB;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE;QACjC,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO;KACvC;IACD,WAAW,EAAE;QACX,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,QAAQ;KACrB;IACD,aAAa,EAAE;QACb,KAAK,EAAE,EAAE;QACT,MAAM,EAAE,EAAE;QACV,YAAY,EAAE,EAAE;QAChB,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,gBAAgB;QAC/C,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;QACpB,WAAW,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE;KACtC;IACD,aAAa,EAAE;QACb,IAAI,EAAE,CAAC;QACP,WAAW,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE;KACtC;CACF,CAAC,CAAC;AAEH,eAAe,mBAAmB,CAAC"}
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { NotificationManager } from '../src/infrastructure/services/NotificationManager';
|
|
2
|
-
import type { ScheduleNotificationOptions } from '../src/infrastructure/services/NotificationManager';
|
|
3
|
-
|
|
4
|
-
// Mock expo-notifications
|
|
5
|
-
jest.mock('expo-notifications', () => ({
|
|
6
|
-
setNotificationHandler: jest.fn(),
|
|
7
|
-
scheduleNotificationAsync: jest.fn(),
|
|
8
|
-
cancelScheduledNotificationAsync: jest.fn(),
|
|
9
|
-
cancelAllScheduledNotificationsAsync: jest.fn(),
|
|
10
|
-
getAllScheduledNotificationsAsync: jest.fn(),
|
|
11
|
-
dismissAllNotificationsAsync: jest.fn(),
|
|
12
|
-
getBadgeCountAsync: jest.fn(),
|
|
13
|
-
setBadgeCountAsync: jest.fn(),
|
|
14
|
-
getPermissionsAsync: jest.fn(),
|
|
15
|
-
requestPermissionsAsync: jest.fn(),
|
|
16
|
-
setNotificationChannelAsync: jest.fn(),
|
|
17
|
-
AndroidImportance: {
|
|
18
|
-
DEFAULT: 'default',
|
|
19
|
-
HIGH: 'high',
|
|
20
|
-
MAX: 'max',
|
|
21
|
-
},
|
|
22
|
-
AndroidNotificationPriority: {
|
|
23
|
-
DEFAULT: 'default',
|
|
24
|
-
HIGH: 'high',
|
|
25
|
-
},
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
// Mock expo-device
|
|
29
|
-
jest.mock('expo-device', () => ({
|
|
30
|
-
isDevice: true,
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
// Mock react-native
|
|
34
|
-
jest.mock('react-native', () => ({
|
|
35
|
-
Platform: {
|
|
36
|
-
OS: 'ios',
|
|
37
|
-
},
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
describe('NotificationManager', () => {
|
|
41
|
-
let manager: NotificationManager;
|
|
42
|
-
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
manager = new NotificationManager();
|
|
45
|
-
jest.clearAllMocks();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe('scheduleNotification', () => {
|
|
49
|
-
it('should schedule a notification with date trigger', async () => {
|
|
50
|
-
const mockSchedule = require('expo-notifications').scheduleNotificationAsync;
|
|
51
|
-
mockSchedule.mockResolvedValue('test-id');
|
|
52
|
-
|
|
53
|
-
const options: ScheduleNotificationOptions = {
|
|
54
|
-
title: 'Test Notification',
|
|
55
|
-
body: 'Test body',
|
|
56
|
-
trigger: { type: 'date', date: new Date('2025-01-15T09:00:00') },
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const result = await manager.scheduleNotification(options);
|
|
60
|
-
|
|
61
|
-
expect(result).toBe('test-id');
|
|
62
|
-
expect(mockSchedule).toHaveBeenCalledWith({
|
|
63
|
-
content: {
|
|
64
|
-
title: 'Test Notification',
|
|
65
|
-
body: 'Test body',
|
|
66
|
-
data: {},
|
|
67
|
-
sound: 'default',
|
|
68
|
-
priority: 'HIGH',
|
|
69
|
-
vibrate: [0, 250, 250, 250],
|
|
70
|
-
},
|
|
71
|
-
trigger: {
|
|
72
|
-
date: new Date('2025-01-15T09:00:00'),
|
|
73
|
-
channelId: 'default',
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should schedule a daily notification', async () => {
|
|
79
|
-
const mockSchedule = require('expo-notifications').scheduleNotificationAsync;
|
|
80
|
-
mockSchedule.mockResolvedValue('daily-id');
|
|
81
|
-
|
|
82
|
-
const options: ScheduleNotificationOptions = {
|
|
83
|
-
title: 'Daily Reminder',
|
|
84
|
-
body: 'Time for daily task',
|
|
85
|
-
trigger: { type: 'daily', hour: 9, minute: 0 },
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const result = await manager.scheduleNotification(options);
|
|
89
|
-
|
|
90
|
-
expect(result).toBe('daily-id');
|
|
91
|
-
expect(mockSchedule).toHaveBeenCalledWith({
|
|
92
|
-
content: expect.objectContaining({
|
|
93
|
-
title: 'Daily Reminder',
|
|
94
|
-
body: 'Time for daily task',
|
|
95
|
-
}),
|
|
96
|
-
trigger: {
|
|
97
|
-
hour: 9,
|
|
98
|
-
minute: 0,
|
|
99
|
-
repeats: true,
|
|
100
|
-
channelId: 'reminders',
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should handle scheduling errors', async () => {
|
|
106
|
-
const mockSchedule = require('expo-notifications').scheduleNotificationAsync;
|
|
107
|
-
mockSchedule.mockRejectedValue(new Error('Scheduling failed'));
|
|
108
|
-
|
|
109
|
-
const options: ScheduleNotificationOptions = {
|
|
110
|
-
title: 'Test',
|
|
111
|
-
body: 'Test',
|
|
112
|
-
trigger: { type: 'date', date: new Date() },
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
await expect(manager.scheduleNotification(options)).rejects.toThrow('Scheduling failed');
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe('cancelNotification', () => {
|
|
120
|
-
it('should cancel a scheduled notification', async () => {
|
|
121
|
-
const mockCancel = require('expo-notifications').cancelScheduledNotificationAsync;
|
|
122
|
-
mockCancel.mockResolvedValue(undefined);
|
|
123
|
-
|
|
124
|
-
await manager.cancelNotification('test-id');
|
|
125
|
-
|
|
126
|
-
expect(mockCancel).toHaveBeenCalledWith('test-id');
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should handle cancellation errors', async () => {
|
|
130
|
-
const mockCancel = require('expo-notifications').cancelScheduledNotificationAsync;
|
|
131
|
-
mockCancel.mockRejectedValue(new Error('Cancel failed'));
|
|
132
|
-
|
|
133
|
-
await expect(manager.cancelNotification('test-id')).rejects.toThrow('Cancel failed');
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
describe('getScheduledNotifications', () => {
|
|
138
|
-
it('should return scheduled notifications', async () => {
|
|
139
|
-
const mockGetAll = require('expo-notifications').getAllScheduledNotificationsAsync;
|
|
140
|
-
mockGetAll.mockResolvedValue([
|
|
141
|
-
{
|
|
142
|
-
identifier: 'test-id',
|
|
143
|
-
content: {
|
|
144
|
-
title: 'Test',
|
|
145
|
-
body: 'Test body',
|
|
146
|
-
data: { key: 'value' },
|
|
147
|
-
},
|
|
148
|
-
trigger: { type: 'date' },
|
|
149
|
-
},
|
|
150
|
-
]);
|
|
151
|
-
|
|
152
|
-
const result = await manager.getScheduledNotifications();
|
|
153
|
-
|
|
154
|
-
expect(result).toHaveLength(1);
|
|
155
|
-
expect(result[0]).toEqual({
|
|
156
|
-
identifier: 'test-id',
|
|
157
|
-
content: {
|
|
158
|
-
title: 'Test',
|
|
159
|
-
body: 'Test body',
|
|
160
|
-
data: { key: 'value' },
|
|
161
|
-
},
|
|
162
|
-
trigger: { type: 'date' },
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should handle empty notifications list', async () => {
|
|
167
|
-
const mockGetAll = require('expo-notifications').getAllScheduledNotificationsAsync;
|
|
168
|
-
mockGetAll.mockResolvedValue([]);
|
|
169
|
-
|
|
170
|
-
const result = await manager.getScheduledNotifications();
|
|
171
|
-
|
|
172
|
-
expect(result).toHaveLength(0);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('should handle errors gracefully', async () => {
|
|
176
|
-
const mockGetAll = require('expo-notifications').getAllScheduledNotificationsAsync;
|
|
177
|
-
mockGetAll.mockRejectedValue(new Error('Fetch failed'));
|
|
178
|
-
|
|
179
|
-
const result = await manager.getScheduledNotifications();
|
|
180
|
-
|
|
181
|
-
expect(result).toHaveLength(0);
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
describe('badge management', () => {
|
|
186
|
-
it('should get badge count on iOS', async () => {
|
|
187
|
-
const mockGetBadge = require('expo-notifications').getBadgeCountAsync;
|
|
188
|
-
mockGetBadge.mockResolvedValue(5);
|
|
189
|
-
|
|
190
|
-
const result = await manager.getBadgeCount();
|
|
191
|
-
|
|
192
|
-
expect(result).toBe(5);
|
|
193
|
-
expect(mockGetBadge).toHaveBeenCalled();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('should return 0 on Android', async () => {
|
|
197
|
-
jest.doMock('react-native', () => ({
|
|
198
|
-
Platform: { OS: 'android' },
|
|
199
|
-
}));
|
|
200
|
-
|
|
201
|
-
const result = await manager.getBadgeCount();
|
|
202
|
-
|
|
203
|
-
expect(result).toBe(0);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('should set badge count on iOS', async () => {
|
|
207
|
-
const mockSetBadge = require('expo-notifications').setBadgeCountAsync;
|
|
208
|
-
mockSetBadge.mockResolvedValue(undefined);
|
|
209
|
-
|
|
210
|
-
await manager.setBadgeCount(3);
|
|
211
|
-
|
|
212
|
-
expect(mockSetBadge).toHaveBeenCalledWith(3);
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
});
|
|
@@ -1,189 +0,0 @@
|
|
|
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
|
-
});
|