@umituz/react-native-settings 4.21.11 → 4.21.13

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 (47) hide show
  1. package/package.json +11 -2
  2. package/src/domains/gamification/components/GamificationScreenWrapper.tsx +58 -85
  3. package/src/domains/gamification/components/index.ts +1 -0
  4. package/src/domains/gamification/index.ts +4 -11
  5. package/src/domains/gamification/types/index.ts +18 -0
  6. package/src/domains/notifications/index.ts +139 -0
  7. package/src/domains/notifications/infrastructure/config/notificationsConfig.ts +98 -0
  8. package/src/domains/notifications/infrastructure/hooks/useNotificationSettings.ts +37 -0
  9. package/src/domains/notifications/infrastructure/services/NotificationBadgeManager.ts +28 -0
  10. package/src/domains/notifications/infrastructure/services/NotificationManager.ts +138 -0
  11. package/src/domains/notifications/infrastructure/services/NotificationPermissions.ts +80 -0
  12. package/src/domains/notifications/infrastructure/services/NotificationScheduler.ts +77 -0
  13. package/src/domains/notifications/infrastructure/services/NotificationService.ts +50 -0
  14. package/src/domains/notifications/infrastructure/services/types.ts +176 -0
  15. package/src/domains/notifications/infrastructure/storage/NotificationsStore.ts +45 -0
  16. package/src/domains/notifications/infrastructure/utils/dev.ts +25 -0
  17. package/src/domains/notifications/infrastructure/utils/idGenerator.ts +14 -0
  18. package/src/domains/notifications/infrastructure/utils/triggerBuilder.ts +45 -0
  19. package/src/domains/notifications/presentation/components/NotificationsSection.tsx +84 -0
  20. package/src/domains/notifications/presentation/components/RemindersNavRow.styles.ts +38 -0
  21. package/src/domains/notifications/presentation/components/RemindersNavRow.tsx +51 -0
  22. package/src/domains/notifications/presentation/components/SettingRow.tsx +86 -0
  23. package/src/domains/notifications/presentation/hooks/useNotificationSettingsUI.ts +52 -0
  24. package/src/domains/notifications/presentation/hooks/useTimePicker.ts +71 -0
  25. package/src/domains/notifications/presentation/screens/NotificationSettingsScreen.styles.ts +30 -0
  26. package/src/domains/notifications/presentation/screens/NotificationSettingsScreen.tsx +131 -0
  27. package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +107 -0
  28. package/src/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.ts +52 -0
  29. package/src/domains/notifications/quietHours/presentation/components/QuietHoursCard.tsx +112 -0
  30. package/src/domains/notifications/reminders/infrastructure/config/reminderPresets.ts +120 -0
  31. package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +106 -0
  32. package/src/domains/notifications/reminders/infrastructure/storage/RemindersStore.ts +148 -0
  33. package/src/domains/notifications/reminders/presentation/components/FormButton.tsx +66 -0
  34. package/src/domains/notifications/reminders/presentation/components/FrequencySelector.tsx +72 -0
  35. package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +169 -0
  36. package/src/domains/notifications/reminders/presentation/components/ReminderItem.tsx +130 -0
  37. package/src/domains/notifications/reminders/presentation/components/TimePresetSelector.tsx +100 -0
  38. package/src/domains/notifications/reminders/presentation/components/WeekdaySelector.tsx +61 -0
  39. package/src/domains/notifications/reminders/presentation/screens/ReminderListScreen.tsx +131 -0
  40. package/src/index.ts +3 -0
  41. package/src/presentation/navigation/SettingsStackNavigator.tsx +21 -11
  42. package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +1 -1
  43. package/src/domains/gamification/README.md +0 -343
  44. package/src/domains/gamification/components/GamificationSettingsItem.tsx +0 -33
  45. package/src/domains/gamification/examples/gamification.config.example.ts +0 -70
  46. package/src/domains/gamification/examples/localization.example.json +0 -71
  47. package/src/domains/gamification/types/settings.ts +0 -28
@@ -0,0 +1,138 @@
1
+ /**
2
+ * NotificationManager - Core Notification Operations
3
+ *
4
+ * Offline-first notification system using expo-notifications.
5
+ * Works in ALL apps (offline, online, hybrid) - no backend required.
6
+ */
7
+
8
+ import * as Notifications from 'expo-notifications';
9
+ import * as Device from 'expo-device';
10
+ import { Platform } from 'react-native';
11
+ import { NotificationPermissions } from './NotificationPermissions';
12
+ import { NotificationScheduler } from './NotificationScheduler';
13
+ import { NotificationBadgeManager } from './NotificationBadgeManager';
14
+ import { devLog, devError } from '../utils/dev';
15
+ import type { NotificationTrigger, ScheduleNotificationOptions, ScheduledNotification } from './types';
16
+
17
+ export class NotificationManager {
18
+ private permissions: NotificationPermissions;
19
+ private scheduler: NotificationScheduler;
20
+ private badgeManager: NotificationBadgeManager;
21
+
22
+ constructor() {
23
+ this.permissions = new NotificationPermissions();
24
+ this.scheduler = new NotificationScheduler();
25
+ this.badgeManager = new NotificationBadgeManager();
26
+ }
27
+
28
+ static configure() {
29
+ Notifications.setNotificationHandler({
30
+ handleNotification: async () => ({
31
+ shouldShowAlert: true,
32
+ shouldPlaySound: true,
33
+ shouldSetBadge: true,
34
+ shouldShowBanner: true,
35
+ shouldShowList: true,
36
+ }),
37
+ });
38
+
39
+ devLog('[NotificationManager] Configured notification handler');
40
+ }
41
+
42
+ async requestPermissions(): Promise<boolean> {
43
+ try {
44
+ const result = await this.permissions.requestPermissions();
45
+
46
+ devLog('[NotificationManager] Permissions requested:', result);
47
+
48
+ return result;
49
+ } catch (error) {
50
+ devError('[NotificationManager] Permission request failed:', error);
51
+ return false;
52
+ }
53
+ }
54
+
55
+ async hasPermissions(): Promise<boolean> {
56
+ try {
57
+ return await this.permissions.hasPermissions();
58
+ } catch (error) {
59
+ devError('[NotificationManager] Permission check failed:', error);
60
+ return false;
61
+ }
62
+ }
63
+
64
+ async scheduleNotification(options: ScheduleNotificationOptions): Promise<string> {
65
+ try {
66
+ const id = await this.scheduler.scheduleNotification(options);
67
+
68
+ devLog('[NotificationManager] Notification scheduled:', id);
69
+
70
+ return id;
71
+ } catch (error) {
72
+ devError('[NotificationManager] Schedule notification failed:', error);
73
+ throw error;
74
+ }
75
+ }
76
+
77
+ async cancelNotification(notificationId: string): Promise<void> {
78
+ try {
79
+ await this.scheduler.cancelNotification(notificationId);
80
+
81
+ devLog('[NotificationManager] Notification cancelled:', notificationId);
82
+ } catch (error) {
83
+ devError('[NotificationManager] Cancel notification failed:', error);
84
+ throw error;
85
+ }
86
+ }
87
+
88
+ async cancelAllNotifications(): Promise<void> {
89
+ try {
90
+ await this.scheduler.cancelAllNotifications();
91
+
92
+ devLog('[NotificationManager] All notifications cancelled');
93
+ } catch (error) {
94
+ devError('[NotificationManager] Cancel all notifications failed:', error);
95
+ throw error;
96
+ }
97
+ }
98
+
99
+ async getScheduledNotifications(): Promise<ScheduledNotification[]> {
100
+ try {
101
+ return await this.scheduler.getScheduledNotifications();
102
+ } catch (error) {
103
+ devError('[NotificationManager] Get scheduled notifications failed:', error);
104
+ return [];
105
+ }
106
+ }
107
+
108
+ async dismissAllNotifications(): Promise<void> {
109
+ try {
110
+ await Notifications.dismissAllNotificationsAsync();
111
+
112
+ devLog('[NotificationManager] All notifications dismissed');
113
+ } catch (error) {
114
+ devError('[NotificationManager] Dismiss all notifications failed:', error);
115
+ throw error;
116
+ }
117
+ }
118
+
119
+ async getBadgeCount(): Promise<number> {
120
+ try {
121
+ return await this.badgeManager.getBadgeCount();
122
+ } catch (error) {
123
+ devError('[NotificationManager] Get badge count failed:', error);
124
+ return 0;
125
+ }
126
+ }
127
+
128
+ async setBadgeCount(count: number): Promise<void> {
129
+ try {
130
+ await this.badgeManager.setBadgeCount(count);
131
+
132
+ devLog('[NotificationManager] Badge count set:', count);
133
+ } catch (error) {
134
+ devError('[NotificationManager] Set badge count failed:', error);
135
+ throw error;
136
+ }
137
+ }
138
+ }
@@ -0,0 +1,80 @@
1
+ import * as Notifications from 'expo-notifications';
2
+ import * as Device from 'expo-device';
3
+ import { Platform } from 'react-native';
4
+ import { devWarn, devError, devLog } from '../utils/dev';
5
+
6
+ export class NotificationPermissions {
7
+ async requestPermissions(): Promise<boolean> {
8
+ try {
9
+ if (!Device.isDevice) {
10
+ devWarn('[NotificationPermissions] Notifications only work on physical devices');
11
+ return false;
12
+ }
13
+
14
+ const permissionsResponse = await Notifications.getPermissionsAsync();
15
+ const existingStatus = (permissionsResponse as any).status || ((permissionsResponse as any).granted ? 'granted' : 'denied');
16
+ let finalStatus = existingStatus;
17
+
18
+ if (existingStatus !== 'granted') {
19
+ const requestResponse = await Notifications.requestPermissionsAsync();
20
+ finalStatus = (requestResponse as any).status || ((requestResponse as any).granted ? 'granted' : 'denied');
21
+ }
22
+
23
+ if (Platform.OS === 'android') {
24
+ await this.createAndroidChannels();
25
+ }
26
+
27
+ return finalStatus === 'granted';
28
+ } catch (error) {
29
+ devError('[NotificationPermissions] Permission request failed:', error);
30
+ return false;
31
+ }
32
+ }
33
+
34
+ async hasPermissions(): Promise<boolean> {
35
+ try {
36
+ if (!Device.isDevice) return false;
37
+ const permissionsResponse = await Notifications.getPermissionsAsync();
38
+ return (permissionsResponse as any).status === 'granted' || (permissionsResponse as any).granted === true;
39
+ } catch (error) {
40
+ devError('[NotificationPermissions] Permission check failed:', error);
41
+ return false;
42
+ }
43
+ }
44
+
45
+ private async createAndroidChannels(): Promise<void> {
46
+ if (Platform.OS !== 'android') return;
47
+
48
+ try {
49
+ await Notifications.setNotificationChannelAsync('default', {
50
+ name: 'Default',
51
+ importance: Notifications.AndroidImportance.DEFAULT,
52
+ vibrationPattern: [0, 250, 250, 250],
53
+ sound: 'default',
54
+ lightColor: '#3B82F6',
55
+ });
56
+
57
+ await Notifications.setNotificationChannelAsync('reminders', {
58
+ name: 'Reminders',
59
+ importance: Notifications.AndroidImportance.HIGH,
60
+ vibrationPattern: [0, 250, 250, 250],
61
+ sound: 'default',
62
+ lightColor: '#3B82F6',
63
+ enableVibrate: true,
64
+ });
65
+
66
+ await Notifications.setNotificationChannelAsync('urgent', {
67
+ name: 'Urgent',
68
+ importance: Notifications.AndroidImportance.MAX,
69
+ vibrationPattern: [0, 500, 250, 500],
70
+ sound: 'default',
71
+ lightColor: '#EF4444',
72
+ enableVibrate: true,
73
+ });
74
+
75
+ devLog('[NotificationPermissions] Android channels created');
76
+ } catch (error) {
77
+ devError('[NotificationPermissions] Android channel creation failed:', error);
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,77 @@
1
+ import * as Notifications from 'expo-notifications';
2
+ import type { NotificationTrigger, ScheduleNotificationOptions, ScheduledNotification } from './types';
3
+
4
+ export class NotificationScheduler {
5
+ async scheduleNotification(options: ScheduleNotificationOptions): Promise<string> {
6
+ const { title, body, data = {}, trigger, sound = true, badge, categoryIdentifier } = options;
7
+
8
+ let notificationTrigger: any;
9
+
10
+ if (trigger.type === 'date') {
11
+ notificationTrigger = {
12
+ date: trigger.date,
13
+ channelId: categoryIdentifier || 'default',
14
+ };
15
+ } else if (trigger.type === 'daily') {
16
+ notificationTrigger = {
17
+ hour: trigger.hour,
18
+ minute: trigger.minute,
19
+ repeats: true,
20
+ channelId: categoryIdentifier || 'reminders',
21
+ };
22
+ } else if (trigger.type === 'weekly') {
23
+ notificationTrigger = {
24
+ weekday: trigger.weekday,
25
+ hour: trigger.hour,
26
+ minute: trigger.minute,
27
+ repeats: true,
28
+ channelId: categoryIdentifier || 'reminders',
29
+ };
30
+ } else if (trigger.type === 'monthly') {
31
+ notificationTrigger = {
32
+ day: trigger.day,
33
+ hour: trigger.hour,
34
+ minute: trigger.minute,
35
+ repeats: true,
36
+ channelId: categoryIdentifier || 'reminders',
37
+ };
38
+ }
39
+
40
+ const notificationId = await Notifications.scheduleNotificationAsync({
41
+ content: {
42
+ title,
43
+ body,
44
+ data,
45
+ sound: sound === true ? 'default' : sound || undefined,
46
+ badge,
47
+ categoryIdentifier,
48
+ priority: Notifications.AndroidNotificationPriority.HIGH,
49
+ vibrate: [0, 250, 250, 250],
50
+ },
51
+ trigger: notificationTrigger,
52
+ });
53
+
54
+ return notificationId;
55
+ }
56
+
57
+ async cancelNotification(notificationId: string): Promise<void> {
58
+ await Notifications.cancelScheduledNotificationAsync(notificationId);
59
+ }
60
+
61
+ async cancelAllNotifications(): Promise<void> {
62
+ await Notifications.cancelAllScheduledNotificationsAsync();
63
+ }
64
+
65
+ async getScheduledNotifications(): Promise<ScheduledNotification[]> {
66
+ const notifications = await Notifications.getAllScheduledNotificationsAsync();
67
+ return notifications.map(notification => ({
68
+ identifier: notification.identifier,
69
+ content: {
70
+ title: notification.content.title || '',
71
+ body: notification.content.body || '',
72
+ data: notification.content.data as Record<string, any>,
73
+ },
74
+ trigger: notification.trigger,
75
+ }));
76
+ }
77
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * NotificationService
3
+ *
4
+ * Simple facade for offline notification system.
5
+ * Works in ALL apps - offline, online, hybrid - no backend required.
6
+ *
7
+ * @module NotificationService
8
+ */
9
+
10
+ import { NotificationManager } from './NotificationManager';
11
+
12
+ export * from './types';
13
+
14
+ /**
15
+ * Notification service singleton
16
+ * Provides simple access to notification manager
17
+ */
18
+ export class NotificationService {
19
+ private static instance: NotificationService;
20
+
21
+ readonly notifications = new NotificationManager();
22
+
23
+ private constructor() {
24
+ // Configure notification handler on initialization
25
+ NotificationManager.configure();
26
+ }
27
+
28
+ static getInstance(): NotificationService {
29
+ if (!NotificationService.instance) {
30
+ NotificationService.instance = new NotificationService();
31
+ }
32
+ return NotificationService.instance;
33
+ }
34
+
35
+ /**
36
+ * Request notification permissions
37
+ */
38
+ async requestPermissions(): Promise<boolean> {
39
+ return await this.notifications.requestPermissions();
40
+ }
41
+
42
+ /**
43
+ * Check if permissions are granted
44
+ */
45
+ async hasPermissions(): Promise<boolean> {
46
+ return await this.notifications.hasPermissions();
47
+ }
48
+ }
49
+
50
+ export const notificationService = NotificationService.getInstance();
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Offline-First Notification Types
3
+ * Uses expo-notifications for local device notifications
4
+ */
5
+
6
+ import * as Notifications from 'expo-notifications';
7
+
8
+ // ============================================================================
9
+ // NOTIFICATION CHANNEL TYPES
10
+ // ============================================================================
11
+
12
+ export interface NotificationChannel {
13
+ id: string;
14
+ channel_type: 'push' | 'in_app';
15
+ channel_address: string;
16
+ preferences: Record<string, unknown>;
17
+ is_verified: boolean;
18
+ is_active: boolean;
19
+ created_at: string;
20
+ }
21
+
22
+ // ============================================================================
23
+ // NOTIFICATION TRIGGER TYPES
24
+ // ============================================================================
25
+
26
+ export type NotificationTrigger =
27
+ | { type: 'date'; date: Date }
28
+ | { type: 'daily'; hour: number; minute: number }
29
+ | { type: 'weekly'; weekday: number; hour: number; minute: number }
30
+ | { type: 'monthly'; day: number; hour: number; minute: number };
31
+
32
+ export interface ScheduleNotificationOptions {
33
+ title: string;
34
+ body: string;
35
+ data?: Record<string, unknown>;
36
+ trigger: NotificationTrigger;
37
+ sound?: boolean | string;
38
+ badge?: number;
39
+ categoryIdentifier?: string;
40
+ }
41
+
42
+ export interface ScheduledNotification {
43
+ identifier: string;
44
+ content: {
45
+ title: string;
46
+ body: string;
47
+ data: Record<string, unknown>;
48
+ };
49
+ trigger: Notifications.NotificationTrigger;
50
+ }
51
+
52
+ // ============================================================================
53
+ // TIME PRESET TYPES
54
+ // ============================================================================
55
+
56
+ export interface TimePreset {
57
+ id: string;
58
+ hour: number;
59
+ minute: number;
60
+ labelKey: string;
61
+ iconName: string;
62
+ }
63
+
64
+ // ============================================================================
65
+ // REMINDER TYPES
66
+ // ============================================================================
67
+
68
+ export type ReminderFrequency = 'once' | 'daily' | 'weekly' | 'monthly';
69
+
70
+ export interface Reminder {
71
+ id: string;
72
+ title: string;
73
+ body: string;
74
+ frequency: ReminderFrequency;
75
+ timePresetId?: string;
76
+ hour: number;
77
+ minute: number;
78
+ weekday?: number;
79
+ dayOfMonth?: number;
80
+ enabled: boolean;
81
+ notificationId?: string;
82
+ createdAt: string;
83
+ updatedAt: string;
84
+ }
85
+
86
+ export interface CreateReminderInput {
87
+ title: string;
88
+ body: string;
89
+ frequency: ReminderFrequency;
90
+ timePresetId?: string;
91
+ hour: number;
92
+ minute: number;
93
+ weekday?: number;
94
+ dayOfMonth?: number;
95
+ }
96
+
97
+ export interface UpdateReminderInput {
98
+ title?: string;
99
+ body?: string;
100
+ frequency?: ReminderFrequency;
101
+ timePresetId?: string;
102
+ hour?: number;
103
+ minute?: number;
104
+ weekday?: number;
105
+ dayOfMonth?: number;
106
+ enabled?: boolean;
107
+ }
108
+
109
+ // ============================================================================
110
+ // QUIET HOURS TYPES
111
+ // ============================================================================
112
+
113
+ export interface QuietHoursConfig {
114
+ enabled: boolean;
115
+ startHour: number;
116
+ startMinute: number;
117
+ endHour: number;
118
+ endMinute: number;
119
+ }
120
+
121
+ // ============================================================================
122
+ // NOTIFICATION PREFERENCES
123
+ // ============================================================================
124
+
125
+ export interface NotificationPreferences {
126
+ enabled: boolean;
127
+ sound: boolean;
128
+ vibration: boolean;
129
+ quietHours: QuietHoursConfig;
130
+ }
131
+
132
+ // ============================================================================
133
+ // SCREEN CONFIGURATION TYPES
134
+ // ============================================================================
135
+
136
+ export interface ReminderTranslations {
137
+ screenTitle: string;
138
+ emptyTitle: string;
139
+ emptyDescription: string;
140
+ addButtonLabel: string;
141
+ editButtonLabel: string;
142
+ deleteButtonLabel: string;
143
+ enabledLabel: string;
144
+ disabledLabel: string;
145
+ frequencyOnce: string;
146
+ frequencyDaily: string;
147
+ frequencyWeekly: string;
148
+ frequencyMonthly: string;
149
+ timeLabel: string;
150
+ titlePlaceholder: string;
151
+ bodyPlaceholder: string;
152
+ saveButtonLabel: string;
153
+ cancelButtonLabel: string;
154
+ }
155
+
156
+ export interface QuietHoursTranslations {
157
+ title: string;
158
+ description: string;
159
+ startTimeLabel: string;
160
+ endTimeLabel: string;
161
+ enabledLabel: string;
162
+ }
163
+
164
+ export interface NotificationSettingsTranslations {
165
+ screenTitle: string;
166
+ masterToggleTitle: string;
167
+ masterToggleDescription: string;
168
+ soundTitle: string;
169
+ soundDescription: string;
170
+ vibrationTitle: string;
171
+ vibrationDescription: string;
172
+ remindersTitle: string;
173
+ remindersDescription: string;
174
+ quietHoursTitle: string;
175
+ quietHoursDescription: string;
176
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Notifications Store - Zustand State Management
3
+ * Simple offline-first notification state
4
+ * NO backend, NO user IDs, NO notification history
5
+ */
6
+
7
+ import { createStore } from '@umituz/react-native-storage';
8
+
9
+ interface NotificationsState {
10
+ hasPermissions: boolean;
11
+ isInitialized: boolean;
12
+ }
13
+
14
+ interface NotificationsActions {
15
+ setPermissions: (granted: boolean) => void;
16
+ setInitialized: (initialized: boolean) => void;
17
+ }
18
+
19
+ export const useNotificationsStore = createStore<NotificationsState, NotificationsActions>({
20
+ name: 'notifications-store',
21
+ initialState: {
22
+ hasPermissions: false,
23
+ isInitialized: false,
24
+ },
25
+ persist: false,
26
+ actions: (set) => ({
27
+ setPermissions: (granted) => set({ hasPermissions: granted }),
28
+ setInitialized: (initialized) => set({ isInitialized: initialized }),
29
+ }),
30
+ });
31
+
32
+ /**
33
+ * Hook for accessing notifications state
34
+ */
35
+ export const useNotifications = () => {
36
+ const store = useNotificationsStore();
37
+ const { hasPermissions, isInitialized, setPermissions, setInitialized } = store;
38
+
39
+ return {
40
+ hasPermissions,
41
+ isInitialized,
42
+ setPermissions,
43
+ setInitialized,
44
+ };
45
+ };
@@ -0,0 +1,25 @@
1
+ export const isDev = () => {
2
+ try {
3
+ return typeof __DEV__ !== 'undefined' && __DEV__;
4
+ } catch {
5
+ return false;
6
+ }
7
+ };
8
+
9
+ export const devLog = (message: string, ...args: any[]) => {
10
+ if (isDev()) {
11
+ console.log(message, ...args);
12
+ }
13
+ };
14
+
15
+ export const devError = (message: string, ...args: any[]) => {
16
+ if (isDev()) {
17
+ console.error(message, ...args);
18
+ }
19
+ };
20
+
21
+ export const devWarn = (message: string, ...args: any[]) => {
22
+ if (isDev()) {
23
+ console.warn(message, ...args);
24
+ }
25
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * ID Generator Utility
3
+ * Generates unique IDs for entities
4
+ */
5
+
6
+ export const generateId = (prefix: string = 'id'): string => {
7
+ const timestamp = Date.now();
8
+ const random = Math.random().toString(36).substring(2, 11);
9
+ return `${prefix}_${timestamp}_${random}`;
10
+ };
11
+
12
+ export const generateReminderId = (): string => {
13
+ return generateId('reminder');
14
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Trigger Builder Utility
3
+ * Builds notification triggers from reminder data
4
+ */
5
+
6
+ import type { NotificationTrigger, Reminder } from '../services/types';
7
+
8
+ export const buildTrigger = (reminder: Reminder): NotificationTrigger => {
9
+ const { frequency, hour, minute, weekday, dayOfMonth } = reminder;
10
+
11
+ switch (frequency) {
12
+ case 'once': {
13
+ const date = new Date();
14
+ date.setHours(hour, minute, 0, 0);
15
+
16
+ if (date <= new Date()) {
17
+ date.setDate(date.getDate() + 1);
18
+ }
19
+
20
+ return { type: 'date', date };
21
+ }
22
+
23
+ case 'daily':
24
+ return { type: 'daily', hour, minute };
25
+
26
+ case 'weekly':
27
+ return {
28
+ type: 'weekly',
29
+ weekday: weekday || 1,
30
+ hour,
31
+ minute,
32
+ };
33
+
34
+ case 'monthly':
35
+ return {
36
+ type: 'monthly',
37
+ day: dayOfMonth || 1,
38
+ hour,
39
+ minute,
40
+ };
41
+
42
+ default:
43
+ return { type: 'daily', hour, minute };
44
+ }
45
+ };