@umituz/react-native-design-system 2.3.13 → 2.3.15

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 (106) hide show
  1. package/package.json +32 -13
  2. package/src/index.ts +116 -0
  3. package/src/layouts/ScreenLayout/ScreenLayout.example.tsx +2 -2
  4. package/src/layouts/ScreenLayout/ScreenLayout.tsx +1 -1
  5. package/src/molecules/animation/core/AnimationCore.ts +29 -0
  6. package/src/molecules/animation/domain/entities/Animation.ts +81 -0
  7. package/src/molecules/animation/domain/entities/Fireworks.ts +44 -0
  8. package/src/molecules/animation/domain/entities/Theme.ts +76 -0
  9. package/src/molecules/animation/index.ts +146 -0
  10. package/src/molecules/animation/infrastructure/services/AnimationConfigService.ts +35 -0
  11. package/src/molecules/animation/infrastructure/services/SpringAnimationConfigService.ts +67 -0
  12. package/src/molecules/animation/infrastructure/services/TimingAnimationConfigService.ts +57 -0
  13. package/src/molecules/animation/infrastructure/services/__tests__/SpringAnimationConfigService.test.ts +114 -0
  14. package/src/molecules/animation/infrastructure/services/__tests__/TimingAnimationConfigService.test.ts +105 -0
  15. package/src/molecules/animation/presentation/components/Fireworks.tsx +126 -0
  16. package/src/molecules/animation/presentation/components/__tests__/Fireworks.test.tsx +189 -0
  17. package/src/molecules/animation/presentation/hooks/__tests__/useAnimation.integration.test.ts +216 -0
  18. package/src/molecules/animation/presentation/hooks/__tests__/useFireworks.test.ts +242 -0
  19. package/src/molecules/animation/presentation/hooks/__tests__/useGesture.test.ts +111 -0
  20. package/src/molecules/animation/presentation/hooks/__tests__/useSpringAnimation.test.ts +131 -0
  21. package/src/molecules/animation/presentation/hooks/__tests__/useTimingAnimation.test.ts +175 -0
  22. package/src/molecules/animation/presentation/hooks/__tests__/useTransformAnimation.test.ts +137 -0
  23. package/src/molecules/animation/presentation/hooks/useAnimation.ts +77 -0
  24. package/src/molecules/animation/presentation/hooks/useFireworks.ts +141 -0
  25. package/src/molecules/animation/presentation/hooks/useGesture.ts +61 -0
  26. package/src/molecules/animation/presentation/hooks/useGestureCreators.ts +163 -0
  27. package/src/molecules/animation/presentation/hooks/useGestureState.ts +53 -0
  28. package/src/molecules/animation/presentation/hooks/useIconAnimations.ts +119 -0
  29. package/src/molecules/animation/presentation/hooks/useModalAnimations.ts +124 -0
  30. package/src/molecules/animation/presentation/hooks/useReanimatedReady.ts +60 -0
  31. package/src/molecules/animation/presentation/hooks/useSpringAnimation.ts +69 -0
  32. package/src/molecules/animation/presentation/hooks/useTimingAnimation.ts +111 -0
  33. package/src/molecules/animation/presentation/hooks/useTransformAnimation.ts +57 -0
  34. package/src/molecules/animation/presentation/providers/AnimationThemeProvider.tsx +62 -0
  35. package/src/molecules/animation/presentation/providers/__tests__/AnimationThemeProvider.test.tsx +165 -0
  36. package/src/molecules/animation/types/global.d.ts +97 -0
  37. package/src/molecules/calendar/domain/entities/CalendarDay.entity.ts +115 -0
  38. package/src/molecules/calendar/domain/entities/CalendarEvent.entity.ts +202 -0
  39. package/src/molecules/calendar/domain/repositories/ICalendarRepository.ts +120 -0
  40. package/src/molecules/calendar/index.ts +98 -0
  41. package/src/molecules/calendar/infrastructure/services/CalendarEvents.ts +196 -0
  42. package/src/molecules/calendar/infrastructure/services/CalendarGeneration.ts +172 -0
  43. package/src/molecules/calendar/infrastructure/services/CalendarPermissions.ts +92 -0
  44. package/src/molecules/calendar/infrastructure/services/CalendarService.ts +161 -0
  45. package/src/molecules/calendar/infrastructure/services/CalendarSync.ts +205 -0
  46. package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts +307 -0
  47. package/src/molecules/calendar/infrastructure/utils/DateUtilities.ts +128 -0
  48. package/src/molecules/calendar/presentation/components/AtomicCalendar.tsx +279 -0
  49. package/src/molecules/calendar/presentation/hooks/useCalendar.ts +356 -0
  50. package/src/molecules/celebration/domain/entities/CelebrationConfig.ts +17 -0
  51. package/src/molecules/celebration/domain/entities/FireworksConfig.ts +32 -0
  52. package/src/molecules/celebration/index.ts +93 -0
  53. package/src/molecules/celebration/infrastructure/services/FireworksConfigService.ts +49 -0
  54. package/src/molecules/celebration/presentation/components/CelebrationFireworksOverlay.tsx +33 -0
  55. package/src/molecules/celebration/presentation/components/CelebrationModal.tsx +78 -0
  56. package/src/molecules/celebration/presentation/components/CelebrationModalContent.tsx +90 -0
  57. package/src/molecules/celebration/presentation/hooks/useCelebrationModalAnimation.ts +49 -0
  58. package/src/molecules/celebration/presentation/hooks/useCelebrationState.ts +45 -0
  59. package/src/molecules/celebration/presentation/styles/CelebrationModalStyles.ts +65 -0
  60. package/src/molecules/countdown/components/Countdown.tsx +128 -0
  61. package/src/molecules/countdown/components/CountdownHeader.tsx +84 -0
  62. package/src/molecules/countdown/components/TimeUnit.tsx +73 -0
  63. package/src/molecules/countdown/hooks/useCountdown.ts +107 -0
  64. package/src/molecules/countdown/index.ts +25 -0
  65. package/src/molecules/countdown/types/CountdownTypes.ts +31 -0
  66. package/src/molecules/countdown/utils/TimeCalculator.ts +46 -0
  67. package/src/molecules/emoji/domain/entities/Emoji.ts +129 -0
  68. package/src/molecules/emoji/index.ts +177 -0
  69. package/src/molecules/emoji/presentation/components/EmojiPicker.tsx +102 -0
  70. package/src/molecules/emoji/presentation/hooks/useEmojiPicker.ts +171 -0
  71. package/src/molecules/index.ts +24 -0
  72. package/src/molecules/long-press-menu/domain/entities/MenuAction.ts +37 -0
  73. package/src/molecules/long-press-menu/index.ts +16 -0
  74. package/src/molecules/navigation/StackNavigator.tsx +75 -0
  75. package/src/molecules/navigation/TabsNavigator.tsx +94 -0
  76. package/src/molecules/navigation/components/FabButton.tsx +45 -0
  77. package/src/molecules/navigation/components/TabLabel.tsx +47 -0
  78. package/src/molecules/navigation/createStackNavigator.ts +20 -0
  79. package/src/molecules/navigation/createTabNavigator.ts +20 -0
  80. package/src/molecules/navigation/hooks/useTabBarStyles.ts +54 -0
  81. package/src/molecules/navigation/index.ts +37 -0
  82. package/src/molecules/navigation/types.ts +118 -0
  83. package/src/molecules/navigation/utils/AppNavigation.ts +101 -0
  84. package/src/molecules/navigation/utils/IconRenderer.ts +50 -0
  85. package/src/molecules/navigation/utils/LabelProcessor.ts +70 -0
  86. package/src/molecules/navigation/utils/NavigationCleanup.ts +62 -0
  87. package/src/molecules/navigation/utils/NavigationTheme.ts +21 -0
  88. package/src/molecules/navigation/utils/NavigationValidator.ts +61 -0
  89. package/src/molecules/navigation/utils/ScreenFactory.ts +115 -0
  90. package/src/molecules/navigation/utils/__tests__/IconRenderer.getIconName.test.ts +109 -0
  91. package/src/molecules/navigation/utils/__tests__/IconRenderer.renderIcon.test.ts +116 -0
  92. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processLabel.test.ts +116 -0
  93. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processTitle.test.ts +59 -0
  94. package/src/molecules/navigation/utils/__tests__/NavigationCleanup.test.ts +271 -0
  95. package/src/molecules/navigation/utils/__tests__/NavigationValidator.test.ts +252 -0
  96. package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +194 -0
  97. package/src/molecules/swipe-actions/index.ts +6 -0
  98. package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +131 -0
  99. package/src/theme/hooks/useResponsiveDesignTokens.ts +1 -1
  100. package/src/utilities/clipboard/ClipboardUtils.ts +71 -0
  101. package/src/utilities/clipboard/index.ts +5 -0
  102. package/src/utilities/index.ts +6 -0
  103. package/src/utilities/sharing/domain/entities/Share.ts +210 -0
  104. package/src/utilities/sharing/index.ts +205 -0
  105. package/src/utilities/sharing/infrastructure/services/SharingService.ts +165 -0
  106. package/src/utilities/sharing/presentation/hooks/useSharing.ts +154 -0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * ICalendarRepository Interface
3
+ *
4
+ * Port (interface) for calendar data access operations.
5
+ * Implementations can use any storage mechanism (AsyncStorage, SQLite, etc.)
6
+ *
7
+ * Design Philosophy:
8
+ * - Repository pattern (DDD)
9
+ * - Storage-agnostic
10
+ * - CRUD operations for calendar events
11
+ * - Query methods for finding events
12
+ */
13
+
14
+ import type {
15
+ CalendarEvent,
16
+ CreateCalendarEventRequest,
17
+ UpdateCalendarEventRequest,
18
+ } from '../entities/CalendarEvent.entity';
19
+
20
+ export interface ICalendarRepository {
21
+ /**
22
+ * Get all calendar events
23
+ * @returns Promise resolving to array of all events
24
+ */
25
+ getAllEvents(): Promise<CalendarEvent[]>;
26
+
27
+ /**
28
+ * Get a calendar event by ID
29
+ * @param id - Event ID
30
+ * @returns Promise resolving to event or null if not found
31
+ */
32
+ getEventById(id: string): Promise<CalendarEvent | null>;
33
+
34
+ /**
35
+ * Get events for a specific date
36
+ * @param date - Date string in YYYY-MM-DD format
37
+ * @returns Promise resolving to array of events for that date
38
+ */
39
+ getEventsByDate(date: string): Promise<CalendarEvent[]>;
40
+
41
+ /**
42
+ * Get events within a date range
43
+ * @param startDate - Start date (YYYY-MM-DD)
44
+ * @param endDate - End date (YYYY-MM-DD)
45
+ * @returns Promise resolving to array of events in range
46
+ */
47
+ getEventsByDateRange(startDate: string, endDate: string): Promise<CalendarEvent[]>;
48
+
49
+ /**
50
+ * Get events for a specific month
51
+ * @param year - Year (e.g., 2024)
52
+ * @param month - Month (0-indexed: 0 = January, 11 = December)
53
+ * @returns Promise resolving to array of events in that month
54
+ */
55
+ getEventsByMonth(year: number, month: number): Promise<CalendarEvent[]>;
56
+
57
+ /**
58
+ * Create a new calendar event
59
+ * @param request - Event creation data
60
+ * @returns Promise resolving to created event
61
+ */
62
+ createEvent(request: CreateCalendarEventRequest): Promise<CalendarEvent>;
63
+
64
+ /**
65
+ * Update an existing calendar event
66
+ * @param request - Event update data (must include id)
67
+ * @returns Promise resolving to updated event
68
+ */
69
+ updateEvent(request: UpdateCalendarEventRequest): Promise<CalendarEvent>;
70
+
71
+ /**
72
+ * Delete a calendar event
73
+ * @param id - Event ID to delete
74
+ * @returns Promise resolving to void
75
+ */
76
+ deleteEvent(id: string): Promise<void>;
77
+
78
+ /**
79
+ * Delete all events for a specific date
80
+ * @param date - Date string in YYYY-MM-DD format
81
+ * @returns Promise resolving to number of deleted events
82
+ */
83
+ deleteEventsByDate(date: string): Promise<number>;
84
+
85
+ /**
86
+ * Mark an event as completed
87
+ * @param id - Event ID
88
+ * @returns Promise resolving to updated event
89
+ */
90
+ completeEvent(id: string): Promise<CalendarEvent>;
91
+
92
+ /**
93
+ * Mark an event as incomplete
94
+ * @param id - Event ID
95
+ * @returns Promise resolving to updated event
96
+ */
97
+ uncompleteEvent(id: string): Promise<CalendarEvent>;
98
+
99
+ /**
100
+ * Search events by title
101
+ * @param query - Search query string
102
+ * @returns Promise resolving to array of matching events
103
+ */
104
+ searchEvents(query: string): Promise<CalendarEvent[]>;
105
+
106
+ /**
107
+ * Get upcoming events (from today onwards)
108
+ * @param limit - Maximum number of events to return
109
+ * @returns Promise resolving to array of upcoming events
110
+ */
111
+ getUpcomingEvents(limit?: number): Promise<CalendarEvent[]>;
112
+
113
+ /**
114
+ * Get completed events count for a date range
115
+ * @param startDate - Start date (YYYY-MM-DD)
116
+ * @param endDate - End date (YYYY-MM-DD)
117
+ * @returns Promise resolving to count of completed events
118
+ */
119
+ getCompletedEventsCount(startDate: string, endDate: string): Promise<number>;
120
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Calendar Domain - Barrel Export
3
+ *
4
+ * Public API for the calendar domain.
5
+ * Provides generic, reusable calendar functionality for all factory-generated apps.
6
+ *
7
+ * Features:
8
+ * - Timezone-aware calendar generation (built-in DateUtilities)
9
+ * - Generic event system (workouts, habits, tasks, etc.)
10
+ * - Month/Week/Day views
11
+ * - Event CRUD operations with persistence
12
+ * - Completion tracking
13
+ * - Recurring events support
14
+ * - **NEW:** System calendar sync (expo-calendar) - iOS/Android only
15
+ *
16
+ * Usage:
17
+ * ```tsx
18
+ * import { useCalendar, AtomicCalendar, useSystemCalendar } from '@umituz/react-native-calendar';
19
+ *
20
+ * const MyScreen = () => {
21
+ * const { days, selectedDate, actions } = useCalendar();
22
+ * const {
23
+ * systemCalendars,
24
+ * permission,
25
+ * requestPermission,
26
+ * syncEventToCalendar,
27
+ * } = useSystemCalendar();
28
+ *
29
+ * // Request permission to access device calendar
30
+ * useEffect(() => {
31
+ * requestPermission();
32
+ * }, []);
33
+ *
34
+ * // Sync event to device calendar
35
+ * const handleCreateEvent = async (eventData) => {
36
+ * const event = await actions.addEvent(eventData);
37
+ * await syncEventToCalendar(event); // Sync to iOS/Android calendar
38
+ * };
39
+ *
40
+ * return (
41
+ * <AtomicCalendar
42
+ * days={days}
43
+ * selectedDate={selectedDate}
44
+ * onDateSelect={actions.setSelectedDate}
45
+ * />
46
+ * );
47
+ * };
48
+ * ```
49
+ */
50
+
51
+ // Domain Entities
52
+ export type {
53
+ CalendarEvent,
54
+ CreateCalendarEventRequest,
55
+ UpdateCalendarEventRequest,
56
+ SystemCalendar,
57
+ CalendarPermissionResult,
58
+ } from './domain/entities/CalendarEvent.entity';
59
+
60
+ export type {
61
+ CalendarDay,
62
+ CalendarMonth,
63
+ CalendarWeek,
64
+ } from './domain/entities/CalendarDay.entity';
65
+
66
+ // Domain Repositories
67
+ export type { ICalendarRepository } from './domain/repositories/ICalendarRepository';
68
+
69
+ // Infrastructure Services
70
+ export { CalendarService } from './infrastructure/services/CalendarService';
71
+ export { CalendarPermissions } from './infrastructure/services/CalendarPermissions';
72
+ export { CalendarEvents } from './infrastructure/services/CalendarEvents';
73
+ export { CalendarGeneration } from './infrastructure/services/CalendarGeneration';
74
+ export { CalendarSync } from './infrastructure/services/CalendarSync';
75
+
76
+ // Infrastructure Utils
77
+ export { DateUtilities } from './infrastructure/utils/DateUtilities';
78
+
79
+ // Infrastructure Storage
80
+ export {
81
+ useCalendarStore,
82
+ type CalendarViewMode,
83
+ } from './infrastructure/storage/CalendarStore';
84
+
85
+ // Presentation Hooks
86
+ export {
87
+ useCalendar,
88
+ useCalendarNavigation,
89
+ useCalendarEvents,
90
+ useSystemCalendar,
91
+ type UseCalendarReturn,
92
+ } from './presentation/hooks/useCalendar';
93
+
94
+ // Presentation Components
95
+ export {
96
+ AtomicCalendar,
97
+ type AtomicCalendarProps,
98
+ } from './presentation/components/AtomicCalendar';
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Calendar Events Service
3
+ *
4
+ * Handles CRUD operations for calendar events.
5
+ *
6
+ * SOLID: Single Responsibility - Only event operations
7
+ * DRY: Centralized event management
8
+ * KISS: Simple event interface
9
+ */
10
+
11
+ import * as Calendar from 'expo-calendar';
12
+ import { Platform } from 'react-native';
13
+ import { DateUtilities } from '../utils/DateUtilities';
14
+ import { CalendarPermissions } from './CalendarPermissions';
15
+ import type { CalendarEvent, SystemCalendar } from '../../domain/entities/CalendarEvent.entity';
16
+
17
+ export class CalendarEvents {
18
+ /**
19
+ * Create event in system calendar
20
+ */
21
+ static async createEvent(
22
+ event: CalendarEvent,
23
+ calendar: SystemCalendar
24
+ ): Promise<{ success: boolean; eventId?: string; error?: string }> {
25
+ try {
26
+ if (Platform.OS === 'web') {
27
+ return { success: false, error: 'Calendar sync not supported on web' };
28
+ }
29
+
30
+ const permission = await CalendarPermissions.requestPermissions();
31
+ if (!permission.granted) {
32
+ return { success: false, error: 'Calendar permission not granted' };
33
+ }
34
+
35
+ const eventData = this.buildEventData(event);
36
+ const systemEventId = await Calendar.createEventAsync(calendar.id, eventData);
37
+
38
+ return {
39
+ success: true,
40
+ eventId: systemEventId
41
+ };
42
+ } catch (error) {
43
+ return {
44
+ success: false,
45
+ error: error instanceof Error ? error.message : 'Failed to create event'
46
+ };
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Update event in system calendar
52
+ */
53
+ static async updateEvent(
54
+ event: CalendarEvent
55
+ ): Promise<{ success: boolean; error?: string }> {
56
+ try {
57
+ if (Platform.OS === 'web' || !event.systemCalendar?.eventId) {
58
+ return { success: false, error: 'No system calendar data' };
59
+ }
60
+
61
+ const permission = await CalendarPermissions.requestPermissions();
62
+ if (!permission.granted) {
63
+ return { success: false, error: 'Calendar permission not granted' };
64
+ }
65
+
66
+ const eventData = this.buildEventData(event);
67
+ await Calendar.updateEventAsync(event.systemCalendar.eventId, eventData);
68
+
69
+ return { success: true };
70
+ } catch (error) {
71
+ return {
72
+ success: false,
73
+ error: error instanceof Error ? error.message : 'Failed to update event'
74
+ };
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Delete event from system calendar
80
+ */
81
+ static async deleteEvent(
82
+ eventId: string
83
+ ): Promise<{ success: boolean; error?: string }> {
84
+ try {
85
+ if (Platform.OS === 'web') {
86
+ return { success: false, error: 'Calendar sync not supported on web' };
87
+ }
88
+
89
+ const permission = await CalendarPermissions.requestPermissions();
90
+ if (!permission.granted) {
91
+ return { success: false, error: 'Calendar permission not granted' };
92
+ }
93
+
94
+ await Calendar.deleteEventAsync(eventId);
95
+ return { success: true };
96
+ } catch (error) {
97
+ return {
98
+ success: false,
99
+ error: error instanceof Error ? error.message : 'Failed to delete event'
100
+ };
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Get events from system calendar
106
+ */
107
+ static async getSystemEvents(
108
+ calendarId: string,
109
+ startDate: Date,
110
+ endDate: Date
111
+ ): Promise<CalendarEvent[]> {
112
+ try {
113
+ if (Platform.OS === 'web') {
114
+ return [];
115
+ }
116
+
117
+ const permission = await CalendarPermissions.hasPermissions();
118
+ if (!permission) {
119
+ return [];
120
+ }
121
+
122
+ const systemEvents = await Calendar.getEventsAsync(
123
+ [calendarId],
124
+ startDate,
125
+ endDate
126
+ );
127
+
128
+ return systemEvents.map((event: Calendar.Event) => this.mapSystemEventToCalendarEvent(event));
129
+ } catch {
130
+ return [];
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Build event data for system calendar
136
+ */
137
+ private static buildEventData(event: CalendarEvent) {
138
+ const [year, month, day] = event.date.split('-').map(Number);
139
+ let startDate = new Date(year, month - 1, day);
140
+ let endDate = new Date(startDate);
141
+
142
+ // Set time if provided
143
+ if (event.time) {
144
+ const [hours, minutes] = event.time.split(':').map(Number);
145
+ startDate.setHours(hours, minutes, 0, 0);
146
+ endDate.setHours(hours, minutes, 0, 0);
147
+ }
148
+
149
+ // Set duration
150
+ if (event.duration) {
151
+ endDate.setMinutes(endDate.getMinutes() + event.duration);
152
+ } else {
153
+ endDate.setHours(endDate.getHours() + 1); // Default 1 hour
154
+ }
155
+
156
+ // Create reminders
157
+ const alarms = event.reminders?.map(minutesBefore => ({
158
+ relativeOffset: -minutesBefore
159
+ }));
160
+
161
+ return {
162
+ title: event.title,
163
+ startDate,
164
+ endDate,
165
+ notes: event.description,
166
+ location: event.location,
167
+ alarms,
168
+ timeZone: DateUtilities.getCurrentTimezone()
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Map system calendar event to domain event
174
+ */
175
+ private static mapSystemEventToCalendarEvent(systemEvent: any): CalendarEvent {
176
+ return {
177
+ id: systemEvent.id,
178
+ title: systemEvent.title || '',
179
+ description: systemEvent.notes || '',
180
+ date: DateUtilities.formatDateToString(systemEvent.startDate),
181
+ time: DateUtilities.formatTimeToString(systemEvent.startDate),
182
+ duration: systemEvent.endDate && systemEvent.startDate
183
+ ? (systemEvent.endDate.getTime() - systemEvent.startDate.getTime()) / (1000 * 60)
184
+ : undefined,
185
+ location: systemEvent.location || '',
186
+ reminders: systemEvent.alarms?.map((alarm: any) => -alarm.relativeOffset) || [],
187
+ createdAt: new Date(),
188
+ updatedAt: new Date(),
189
+ systemCalendar: {
190
+ calendarId: systemEvent.calendarId,
191
+ eventId: systemEvent.id,
192
+ lastSyncedAt: new Date()
193
+ }
194
+ };
195
+ }
196
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Calendar Generation Service
3
+ *
4
+ * Generates calendar grids and layouts.
5
+ *
6
+ * SOLID: Single Responsibility - Only calendar generation
7
+ * DRY: Reusable calendar generation logic
8
+ * KISS: Simple calendar grid creation
9
+ */
10
+
11
+ import { DateUtilities } from '../utils/DateUtilities';
12
+ import type { CalendarDay } from '../../domain/entities/CalendarDay.entity';
13
+ import type { CalendarEvent } from '../../domain/entities/CalendarEvent.entity';
14
+
15
+ export class CalendarGeneration {
16
+ /**
17
+ * Generate calendar days for a specific month
18
+ * Returns 42 days (6 weeks) including days from prev/next months
19
+ */
20
+ static generateMonthDays(
21
+ year: number,
22
+ month: number,
23
+ events: CalendarEvent[] = []
24
+ ): CalendarDay[] {
25
+ const firstDay = new Date(year, month, 1);
26
+ const startDate = new Date(firstDay);
27
+
28
+ // Start from Sunday of the week containing the 1st
29
+ startDate.setDate(startDate.getDate() - firstDay.getDay());
30
+
31
+ const days: CalendarDay[] = [];
32
+ const today = new Date();
33
+
34
+ // Generate 42 days (6 weeks)
35
+ for (let i = 0; i < 42; i++) {
36
+ const currentDate = DateUtilities.addDays(startDate, i);
37
+ const dayEvents = this.getEventsForDate(currentDate, events);
38
+
39
+ days.push({
40
+ date: currentDate,
41
+ isCurrentMonth: currentDate.getMonth() === month,
42
+ isToday: DateUtilities.isSameDay(currentDate, today),
43
+ isSelected: false,
44
+ events: dayEvents
45
+ });
46
+ }
47
+
48
+ return days;
49
+ }
50
+
51
+ /**
52
+ * Generate calendar days for a specific week
53
+ */
54
+ static generateWeekDays(
55
+ startDate: Date,
56
+ events: CalendarEvent[] = []
57
+ ): CalendarDay[] {
58
+ const days: CalendarDay[] = [];
59
+ const today = new Date();
60
+
61
+ for (let i = 0; i < 7; i++) {
62
+ const currentDate = DateUtilities.addDays(startDate, i);
63
+ const dayEvents = this.getEventsForDate(currentDate, events);
64
+
65
+ days.push({
66
+ date: currentDate,
67
+ isCurrentMonth: true, // All days in week view are "current"
68
+ isToday: DateUtilities.isSameDay(currentDate, today),
69
+ isSelected: false,
70
+ events: dayEvents
71
+ });
72
+ }
73
+
74
+ return days;
75
+ }
76
+
77
+ /**
78
+ * Get events for a specific date
79
+ */
80
+ static getEventsForDate(date: Date, allEvents: CalendarEvent[]): CalendarEvent[] {
81
+ const dateStr = DateUtilities.formatDateToString(date);
82
+ return allEvents.filter(event => event.date === dateStr);
83
+ }
84
+
85
+ /**
86
+ * Get events in date range
87
+ */
88
+ static getEventsInRange(
89
+ startDate: Date,
90
+ endDate: Date,
91
+ events: CalendarEvent[]
92
+ ): CalendarEvent[] {
93
+ const startStr = DateUtilities.formatDateToString(startDate);
94
+ const endStr = DateUtilities.formatDateToString(endDate);
95
+
96
+ return events.filter(event => event.date >= startStr && event.date <= endStr);
97
+ }
98
+
99
+ /**
100
+ * Navigate to previous month
101
+ */
102
+ static getPreviousMonth(currentDate: Date): Date {
103
+ const newDate = new Date(currentDate);
104
+ newDate.setMonth(newDate.getMonth() - 1);
105
+ return newDate;
106
+ }
107
+
108
+ /**
109
+ * Navigate to next month
110
+ */
111
+ static getNextMonth(currentDate: Date): Date {
112
+ const newDate = new Date(currentDate);
113
+ newDate.setMonth(newDate.getMonth() + 1);
114
+ return newDate;
115
+ }
116
+
117
+ /**
118
+ * Navigate to previous week
119
+ */
120
+ static getPreviousWeek(currentDate: Date): Date {
121
+ return DateUtilities.addDays(currentDate, -7);
122
+ }
123
+
124
+ /**
125
+ * Navigate to next week
126
+ */
127
+ static getNextWeek(currentDate: Date): Date {
128
+ return DateUtilities.addDays(currentDate, 7);
129
+ }
130
+
131
+ /**
132
+ * Check if date is today
133
+ */
134
+ static isToday(date: Date): boolean {
135
+ return DateUtilities.isToday(date);
136
+ }
137
+
138
+ /**
139
+ * Check if two dates are the same day
140
+ */
141
+ static isSameDay(date1: Date, date2: Date): boolean {
142
+ return DateUtilities.isSameDay(date1, date2);
143
+ }
144
+
145
+ /**
146
+ * Format date for display
147
+ */
148
+ static formatDate(date: Date): string {
149
+ return DateUtilities.formatDateToString(date);
150
+ }
151
+
152
+ /**
153
+ * Get month name
154
+ */
155
+ static getMonthName(date: Date): string {
156
+ return date.toLocaleDateString('en-US', { month: 'long' });
157
+ }
158
+
159
+ /**
160
+ * Get weekday name
161
+ */
162
+ static getWeekdayName(date: Date): string {
163
+ return date.toLocaleDateString('en-US', { weekday: 'long' });
164
+ }
165
+
166
+ /**
167
+ * Get short weekday name
168
+ */
169
+ static getShortWeekdayName(date: Date): string {
170
+ return date.toLocaleDateString('en-US', { weekday: 'short' });
171
+ }
172
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Calendar Permissions Service
3
+ *
4
+ * Handles calendar permission requests and status checks.
5
+ *
6
+ * SOLID: Single Responsibility - Only permission operations
7
+ * DRY: Centralized permission logic
8
+ * KISS: Simple permission interface
9
+ */
10
+
11
+ import * as Calendar from 'expo-calendar';
12
+ import { Platform } from 'react-native';
13
+ import type { CalendarPermissionResult } from '../../domain/entities/CalendarEvent.entity';
14
+
15
+ export class CalendarPermissions {
16
+ /**
17
+ * Request calendar permissions
18
+ *
19
+ * @returns Promise with permission result
20
+ */
21
+ static async requestPermissions(): Promise<CalendarPermissionResult> {
22
+ try {
23
+ if (Platform.OS === 'web') {
24
+ return {
25
+ granted: false,
26
+ canAskAgain: false,
27
+ status: 'denied'
28
+ };
29
+ }
30
+
31
+ const { status, canAskAgain } = await Calendar.requestCalendarPermissionsAsync();
32
+
33
+ return {
34
+ granted: status === 'granted',
35
+ canAskAgain,
36
+ status
37
+ };
38
+ } catch {
39
+ return {
40
+ granted: false,
41
+ canAskAgain: false,
42
+ status: 'error'
43
+ };
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Check if calendar permissions are granted
49
+ *
50
+ * @returns Promise with permission status
51
+ */
52
+ static async hasPermissions(): Promise<boolean> {
53
+ try {
54
+ if (Platform.OS === 'web') {
55
+ return false;
56
+ }
57
+
58
+ const { status } = await Calendar.getCalendarPermissionsAsync();
59
+ return status === 'granted';
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Get current permission status
67
+ *
68
+ * @returns Promise with current status
69
+ */
70
+ static async getPermissionStatus(): Promise<CalendarPermissionResult['status']> {
71
+ try {
72
+ if (Platform.OS === 'web') {
73
+ return 'denied';
74
+ }
75
+
76
+ const { status } = await Calendar.getCalendarPermissionsAsync();
77
+ return status;
78
+ } catch {
79
+ return 'error';
80
+ }
81
+ }
82
+ }
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+