@umituz/react-native-design-system 2.3.12 → 2.3.14
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 +14 -12
- package/src/atoms/AtomicAvatar.tsx +1 -1
- package/src/atoms/AtomicBadge.tsx +1 -1
- package/src/atoms/AtomicButton.tsx +1 -1
- package/src/atoms/AtomicCard.tsx +1 -1
- package/src/atoms/AtomicChip.tsx +1 -1
- package/src/atoms/AtomicDatePicker.tsx +1 -1
- package/src/atoms/AtomicFab.tsx +2 -2
- package/src/atoms/AtomicIcon.tsx +1 -1
- package/src/atoms/AtomicInput.tsx +1 -1
- package/src/atoms/AtomicPicker.tsx +1 -1
- package/src/atoms/AtomicProgress.tsx +1 -1
- package/src/atoms/AtomicText.tsx +3 -3
- package/src/atoms/datepicker/components/DatePickerButton.tsx +1 -1
- package/src/atoms/datepicker/components/DatePickerModal.tsx +1 -1
- package/src/atoms/input/styles/inputStylesHelper.ts +1 -1
- package/src/atoms/picker/components/PickerChips.tsx +1 -1
- package/src/atoms/picker/components/PickerModal.tsx +1 -1
- package/src/atoms/picker/styles/pickerStyles.ts +1 -1
- package/src/atoms/skeleton/AtomicSkeleton.tsx +1 -1
- package/src/device/detection/deviceDetection.ts +2 -2
- package/src/device/detection/iPadDetection.ts +1 -1
- package/src/device/detection/iPadLayoutUtils.ts +1 -1
- package/src/index.ts +25 -0
- package/src/layouts/AppHeader/AppHeader.tsx +2 -2
- package/src/layouts/Container/Container.tsx +2 -2
- package/src/layouts/FormLayout/FormLayout.tsx +2 -2
- package/src/layouts/Grid/Grid.tsx +2 -2
- package/src/layouts/ScreenHeader/ScreenHeader.tsx +2 -2
- package/src/layouts/ScreenLayout/ScreenLayout.tsx +3 -3
- package/src/molecules/BaseModal.tsx +2 -2
- package/src/molecules/ConfirmationModalContent.tsx +1 -1
- package/src/molecules/ConfirmationModalMain.tsx +1 -1
- package/src/molecules/Divider/Divider.tsx +2 -2
- package/src/molecules/FormField.tsx +2 -2
- package/src/molecules/IconContainer.tsx +1 -1
- package/src/molecules/List/List.tsx +1 -1
- package/src/molecules/ListItem.tsx +2 -2
- package/src/molecules/SearchBar/SearchBar.tsx +2 -2
- package/src/molecules/SearchBar/SearchHistory.tsx +3 -3
- package/src/molecules/SearchBar/SearchSuggestions.tsx +2 -2
- package/src/molecules/StepProgress/StepProgress.tsx +1 -1
- package/src/molecules/alerts/AlertBanner.tsx +2 -2
- package/src/molecules/alerts/AlertContainer.tsx +1 -1
- package/src/molecules/alerts/AlertInline.tsx +2 -2
- package/src/molecules/alerts/AlertModal.tsx +2 -2
- package/src/molecules/alerts/AlertToast.tsx +2 -2
- package/src/molecules/avatar/Avatar.tsx +2 -2
- package/src/molecules/avatar/AvatarGroup.tsx +2 -2
- package/src/molecules/bottom-sheet/components/BottomSheet.tsx +1 -1
- package/src/molecules/bottom-sheet/components/BottomSheetModal.tsx +1 -1
- package/src/molecules/bottom-sheet/components/filter/FilterBottomSheet.tsx +2 -2
- package/src/molecules/bottom-sheet/components/filter/FilterSheet.tsx +2 -2
- package/src/molecules/bottom-sheet/components/filter/FilterSheetComponents/FilterSheetHeader.tsx +2 -2
- package/src/molecules/bottom-sheet/components/filter/FilterSheetComponents/FilterSheetOption.tsx +2 -2
- package/src/molecules/calendar/domain/entities/CalendarDay.entity.ts +115 -0
- package/src/molecules/calendar/domain/entities/CalendarEvent.entity.ts +202 -0
- package/src/molecules/calendar/domain/repositories/ICalendarRepository.ts +120 -0
- package/src/molecules/calendar/index.ts +98 -0
- package/src/molecules/calendar/infrastructure/services/CalendarEvents.ts +196 -0
- package/src/molecules/calendar/infrastructure/services/CalendarGeneration.ts +172 -0
- package/src/molecules/calendar/infrastructure/services/CalendarPermissions.ts +92 -0
- package/src/molecules/calendar/infrastructure/services/CalendarService.ts +161 -0
- package/src/molecules/calendar/infrastructure/services/CalendarSync.ts +205 -0
- package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts +307 -0
- package/src/molecules/calendar/infrastructure/utils/DateUtilities.ts +128 -0
- package/src/molecules/calendar/presentation/components/AtomicCalendar.tsx +279 -0
- package/src/molecules/calendar/presentation/hooks/useCalendar.ts +356 -0
- package/src/molecules/confirmation-modal/components.tsx +2 -2
- package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +1 -1
- package/src/molecules/index.ts +3 -0
- package/src/molecules/listitem/styles/listItemStyles.ts +1 -1
- package/src/organisms/FormContainer.tsx +2 -2
- package/src/responsive/gridUtils.ts +1 -1
- package/src/responsive/responsiveLayout.ts +1 -1
- package/src/responsive/responsiveModal.ts +1 -1
- package/src/responsive/responsiveSizing.ts +1 -1
- package/src/responsive/useResponsive.ts +1 -1
- package/src/theme/hooks/useResponsiveDesignTokens.ts +1 -1
- package/src/typography/presentation/utils/textColorUtils.ts +1 -1
- package/src/typography/presentation/utils/textStyleUtils.ts +1 -1
|
@@ -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
|
+
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calendar Service
|
|
3
|
+
*
|
|
4
|
+
* Facade for calendar operations using composition.
|
|
5
|
+
* Delegates to specialized services for specific operations.
|
|
6
|
+
*
|
|
7
|
+
* SOLID: Facade pattern - Single entry point, delegates to specialists
|
|
8
|
+
* DRY: Avoids code duplication by composing smaller services
|
|
9
|
+
* KISS: Simple interface, complex operations delegated
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { CalendarDay, CalendarWeek } from '../../domain/entities/CalendarDay.entity';
|
|
13
|
+
import type { CalendarEvent, SystemCalendar } from '../../domain/entities/CalendarEvent.entity';
|
|
14
|
+
import { CalendarGeneration } from './CalendarGeneration';
|
|
15
|
+
import { CalendarPermissions } from './CalendarPermissions';
|
|
16
|
+
// CalendarEvents is not used in this facade
|
|
17
|
+
// import { CalendarEvents } from './CalendarEvents';
|
|
18
|
+
import { CalendarSync } from './CalendarSync';
|
|
19
|
+
import { DateUtilities } from '../utils/DateUtilities';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Calendar Service Implementation
|
|
23
|
+
*
|
|
24
|
+
* Facade that delegates to specialized services.
|
|
25
|
+
* Follows SOLID principles with composition over inheritance.
|
|
26
|
+
*/
|
|
27
|
+
export class CalendarService {
|
|
28
|
+
/**
|
|
29
|
+
* Generate calendar days for a specific month
|
|
30
|
+
*/
|
|
31
|
+
static getMonthDays(
|
|
32
|
+
year: number,
|
|
33
|
+
month: number,
|
|
34
|
+
events: CalendarEvent[] = []
|
|
35
|
+
): CalendarDay[] {
|
|
36
|
+
return CalendarGeneration.generateMonthDays(year, month, events);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate calendar week
|
|
41
|
+
*/
|
|
42
|
+
static getWeek(date: Date, events: CalendarEvent[] = []): CalendarWeek {
|
|
43
|
+
const startDate = DateUtilities.getStartOfWeek(date);
|
|
44
|
+
const endDate = DateUtilities.getEndOfWeek(date);
|
|
45
|
+
const days = CalendarGeneration.generateWeekDays(startDate, events);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
startDate,
|
|
49
|
+
endDate,
|
|
50
|
+
days
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Navigate to previous month
|
|
56
|
+
*/
|
|
57
|
+
static getPreviousMonth(currentDate: Date): Date {
|
|
58
|
+
return CalendarGeneration.getPreviousMonth(currentDate);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Navigate to next month
|
|
63
|
+
*/
|
|
64
|
+
static getNextMonth(currentDate: Date): Date {
|
|
65
|
+
return CalendarGeneration.getNextMonth(currentDate);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Navigate to previous week
|
|
70
|
+
*/
|
|
71
|
+
static getPreviousWeek(currentDate: Date): Date {
|
|
72
|
+
return CalendarGeneration.getPreviousWeek(currentDate);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Navigate to next week
|
|
77
|
+
*/
|
|
78
|
+
static getNextWeek(currentDate: Date): Date {
|
|
79
|
+
return CalendarGeneration.getNextWeek(currentDate);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Request calendar permissions
|
|
84
|
+
*/
|
|
85
|
+
static async requestPermissions() {
|
|
86
|
+
return CalendarPermissions.requestPermissions();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if permissions are granted
|
|
91
|
+
*/
|
|
92
|
+
static async hasPermissions(): Promise<boolean> {
|
|
93
|
+
return CalendarPermissions.hasPermissions();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Sync event to system calendar
|
|
98
|
+
*/
|
|
99
|
+
static async syncToSystemCalendar(event: CalendarEvent) {
|
|
100
|
+
return CalendarSync.syncToSystemCalendar(event);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Update system calendar event
|
|
105
|
+
*/
|
|
106
|
+
static async updateSystemCalendarEvent(event: CalendarEvent) {
|
|
107
|
+
return CalendarSync.updateSystemCalendarEvent(event);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Remove event from system calendar
|
|
112
|
+
*/
|
|
113
|
+
static async removeFromSystemCalendar(eventId: string) {
|
|
114
|
+
return CalendarSync.removeFromSystemCalendar(eventId);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get system calendars
|
|
119
|
+
*/
|
|
120
|
+
static async getSystemCalendars(): Promise<SystemCalendar[]> {
|
|
121
|
+
return CalendarSync.getSystemCalendars();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get events for a specific date
|
|
126
|
+
*/
|
|
127
|
+
static getEventsForDate(date: Date, events: CalendarEvent[]): CalendarEvent[] {
|
|
128
|
+
return CalendarGeneration.getEventsForDate(date, events);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get events in date range
|
|
133
|
+
*/
|
|
134
|
+
static getEventsInRange(
|
|
135
|
+
startDate: Date,
|
|
136
|
+
endDate: Date,
|
|
137
|
+
events: CalendarEvent[]
|
|
138
|
+
): CalendarEvent[] {
|
|
139
|
+
return CalendarGeneration.getEventsInRange(startDate, endDate, events);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get weekday names
|
|
144
|
+
*/
|
|
145
|
+
static getWeekdayNames(): string[] {
|
|
146
|
+
const weekdays: string[] = [];
|
|
147
|
+
for (let i = 0; i < 7; i++) {
|
|
148
|
+
const date = new Date();
|
|
149
|
+
date.setDate(date.getDate() - date.getDay() + i);
|
|
150
|
+
weekdays.push(date.toLocaleDateString('en-US', { weekday: 'short' }));
|
|
151
|
+
}
|
|
152
|
+
return weekdays;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Check if two dates are the same day
|
|
157
|
+
*/
|
|
158
|
+
static isSameDay(date1: Date, date2: Date): boolean {
|
|
159
|
+
return DateUtilities.isSameDay(date1, date2);
|
|
160
|
+
}
|
|
161
|
+
}
|