@umituz/react-native-design-system 2.3.16 → 2.3.17

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "2.3.16",
3
+ "version": "2.3.17",
4
4
  "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -60,7 +60,6 @@
60
60
  "@umituz/react-native-localization": "latest",
61
61
  "@umituz/react-native-uuid": "latest",
62
62
  "expo-application": ">=5.0.0",
63
- "expo-calendar": ">=13.0.0",
64
63
  "expo-clipboard": ">=8.0.0",
65
64
  "expo-crypto": ">=13.0.0",
66
65
  "expo-device": ">=5.0.0",
@@ -114,7 +113,6 @@
114
113
  "eslint-plugin-react-hooks": "^7.0.1",
115
114
  "eslint-plugin-react-native": "^5.0.0",
116
115
  "expo-application": "~5.9.1",
117
- "expo-calendar": "~14.0.0",
118
116
  "expo-clipboard": "~8.0.7",
119
117
  "expo-crypto": "~14.0.0",
120
118
  "expo-device": "~7.0.2",
@@ -74,26 +74,6 @@ export interface CalendarEvent {
74
74
  */
75
75
  reminders?: number[];
76
76
 
77
- /**
78
- * System calendar sync (expo-calendar integration)
79
- */
80
- systemCalendar?: {
81
- /**
82
- * System calendar ID where event is synced
83
- */
84
- calendarId: string;
85
-
86
- /**
87
- * System event ID from device calendar
88
- */
89
- eventId: string;
90
-
91
- /**
92
- * Last sync timestamp
93
- */
94
- lastSyncedAt: Date;
95
- };
96
-
97
77
  /**
98
78
  * App-specific metadata
99
79
  * Use this for domain-specific properties
@@ -158,7 +138,6 @@ export interface CreateCalendarEventRequest {
158
138
  reminders?: number[];
159
139
  metadata?: unknown;
160
140
  recurring?: CalendarEvent['recurring'];
161
- syncToSystemCalendar?: boolean; // Auto-sync to device calendar
162
141
  }
163
142
 
164
143
  /**
@@ -177,26 +156,4 @@ export interface UpdateCalendarEventRequest {
177
156
  reminders?: number[];
178
157
  metadata?: unknown;
179
158
  recurring?: CalendarEvent['recurring'];
180
- systemCalendar?: CalendarEvent['systemCalendar'];
181
- }
182
-
183
- /**
184
- * System calendar list item
185
- */
186
- export interface SystemCalendar {
187
- id: string;
188
- title: string;
189
- color: string;
190
- source: string;
191
- isPrimary: boolean;
192
- allowsModifications: boolean;
193
- }
194
-
195
- /**
196
- * Calendar permission result
197
- */
198
- export interface CalendarPermissionResult {
199
- granted: boolean;
200
- canAskAgain: boolean;
201
- status: string;
202
159
  }
@@ -11,31 +11,13 @@
11
11
  * - Event CRUD operations with persistence
12
12
  * - Completion tracking
13
13
  * - Recurring events support
14
- * - **NEW:** System calendar sync (expo-calendar) - iOS/Android only
15
14
  *
16
15
  * Usage:
17
16
  * ```tsx
18
- * import { useCalendar, AtomicCalendar, useSystemCalendar } from '@umituz/react-native-calendar';
17
+ * import { useCalendar, AtomicCalendar } from '@umituz/react-native-design-system';
19
18
  *
20
19
  * const MyScreen = () => {
21
20
  * 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
21
  *
40
22
  * return (
41
23
  * <AtomicCalendar
@@ -53,8 +35,6 @@ export type {
53
35
  CalendarEvent,
54
36
  CreateCalendarEventRequest,
55
37
  UpdateCalendarEventRequest,
56
- SystemCalendar,
57
- CalendarPermissionResult,
58
38
  } from './domain/entities/CalendarEvent.entity';
59
39
 
60
40
  export type {
@@ -68,10 +48,7 @@ export type { ICalendarRepository } from './domain/repositories/ICalendarReposit
68
48
 
69
49
  // Infrastructure Services
70
50
  export { CalendarService } from './infrastructure/services/CalendarService';
71
- export { CalendarPermissions } from './infrastructure/services/CalendarPermissions';
72
- export { CalendarEvents } from './infrastructure/services/CalendarEvents';
73
51
  export { CalendarGeneration } from './infrastructure/services/CalendarGeneration';
74
- export { CalendarSync } from './infrastructure/services/CalendarSync';
75
52
 
76
53
  // Infrastructure Utils
77
54
  export { DateUtilities } from './infrastructure/utils/DateUtilities';
@@ -87,7 +64,6 @@ export {
87
64
  useCalendar,
88
65
  useCalendarNavigation,
89
66
  useCalendarEvents,
90
- useSystemCalendar,
91
67
  type UseCalendarReturn,
92
68
  } from './presentation/hooks/useCalendar';
93
69
 
@@ -10,12 +10,8 @@
10
10
  */
11
11
 
12
12
  import type { CalendarDay, CalendarWeek } from '../../domain/entities/CalendarDay.entity';
13
- import type { CalendarEvent, SystemCalendar } from '../../domain/entities/CalendarEvent.entity';
13
+ import type { CalendarEvent } from '../../domain/entities/CalendarEvent.entity';
14
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
15
  import { DateUtilities } from '../utils/DateUtilities';
20
16
 
21
17
  /**
@@ -79,48 +75,6 @@ export class CalendarService {
79
75
  return CalendarGeneration.getNextWeek(currentDate);
80
76
  }
81
77
 
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
78
  /**
125
79
  * Get events for a specific date
126
80
  */
@@ -26,15 +26,11 @@
26
26
  * ```
27
27
  */
28
28
 
29
- import { useMemo, useEffect, useState, useCallback } from 'react';
29
+ import { useMemo, useEffect } from 'react';
30
30
  import { useCalendarStore, type CalendarViewMode } from '../../infrastructure/storage/CalendarStore';
31
31
  import { CalendarService } from '../../infrastructure/services/CalendarService';
32
32
  import type { CalendarDay } from '../../domain/entities/CalendarDay.entity';
33
- import type {
34
- CalendarEvent,
35
- SystemCalendar,
36
- CalendarPermissionResult,
37
- } from '../../domain/entities/CalendarEvent.entity';
33
+ import type { CalendarEvent } from '../../domain/entities/CalendarEvent.entity';
38
34
 
39
35
  /**
40
36
  * Calendar hook return type
@@ -182,175 +178,3 @@ export const useCalendarEvents = () => {
182
178
  clearError,
183
179
  };
184
180
  };
185
-
186
- /**
187
- * Hook for system calendar integration (expo-calendar)
188
- *
189
- * USAGE:
190
- * ```tsx
191
- * const {
192
- * systemCalendars,
193
- * permission,
194
- * requestPermission,
195
- * syncEventToCalendar,
196
- * updateSyncedEvent,
197
- * deleteSyncedEvent,
198
- * } = useSystemCalendar();
199
- *
200
- * // Request permission
201
- * const granted = await requestPermission();
202
- *
203
- * // Sync event to device calendar
204
- * await syncEventToCalendar(event);
205
- * ```
206
- */
207
- export const useSystemCalendar = () => {
208
- const [systemCalendars, setSystemCalendars] = useState<SystemCalendar[]>([]);
209
- const [permission, setPermission] = useState<CalendarPermissionResult | null>(null);
210
- const [isLoading, setIsLoading] = useState(false);
211
-
212
- const { actions } = useCalendarStore((state) => state);
213
-
214
- /**
215
- * Request calendar permissions
216
- */
217
- const requestPermission = useCallback(async (): Promise<boolean> => {
218
- setIsLoading(true);
219
- try {
220
- const result = await CalendarService.requestPermissions();
221
- setPermission(result);
222
- return result.granted;
223
- } catch {
224
- return false;
225
- } finally {
226
- setIsLoading(false);
227
- }
228
- }, []);
229
-
230
- /**
231
- * Load system calendars
232
- */
233
- const loadSystemCalendars = useCallback(async () => {
234
- setIsLoading(true);
235
- try {
236
- const calendars = await CalendarService.getSystemCalendars();
237
- setSystemCalendars(calendars);
238
- } catch {
239
- setSystemCalendars([]);
240
- } finally {
241
- setIsLoading(false);
242
- }
243
- }, []);
244
-
245
- /**
246
- * Sync event to system calendar
247
- */
248
- const syncEventToCalendar = useCallback(
249
- async (event: CalendarEvent): Promise<boolean> => {
250
- setIsLoading(true);
251
- try {
252
- const result = await CalendarService.syncToSystemCalendar(event);
253
-
254
- if (result.success && result.eventId && result.calendarId) {
255
- // Update event with system calendar info
256
- await actions.updateEvent({
257
- id: event.id,
258
- systemCalendar: {
259
- eventId: result.eventId,
260
- calendarId: result.calendarId,
261
- lastSyncedAt: new Date(),
262
- },
263
- });
264
- return true;
265
- }
266
-
267
- return false;
268
- } catch {
269
- return false;
270
- } finally {
271
- setIsLoading(false);
272
- }
273
- },
274
- [actions]
275
- );
276
-
277
- /**
278
- * Update synced event in system calendar
279
- */
280
- const updateSyncedEvent = useCallback(async (event: CalendarEvent): Promise<boolean> => {
281
- if (!event.systemCalendar) return false;
282
-
283
- setIsLoading(true);
284
- try {
285
- const result = await CalendarService.updateSystemCalendarEvent(event);
286
-
287
- if (result.success) {
288
- // Update last synced timestamp
289
- await actions.updateEvent({
290
- id: event.id,
291
- systemCalendar: {
292
- ...event.systemCalendar,
293
- lastSyncedAt: new Date(),
294
- },
295
- });
296
- return true;
297
- }
298
-
299
- return false;
300
- } catch {
301
- return false;
302
- } finally {
303
- setIsLoading(false);
304
- }
305
- }, [actions]);
306
-
307
- /**
308
- * Delete synced event from system calendar
309
- */
310
- const deleteSyncedEvent = useCallback(
311
- async (event: CalendarEvent): Promise<boolean> => {
312
- if (!event.systemCalendar) return false;
313
-
314
- setIsLoading(true);
315
- try {
316
- const result = await CalendarService.removeFromSystemCalendar(
317
- event.systemCalendar.eventId
318
- );
319
-
320
- if (result.success) {
321
- // Remove system calendar info from event
322
- await actions.updateEvent({
323
- id: event.id,
324
- systemCalendar: undefined,
325
- });
326
- return true;
327
- }
328
-
329
- return false;
330
- } catch {
331
- return false;
332
- } finally {
333
- setIsLoading(false);
334
- }
335
- },
336
- [actions]
337
- );
338
-
339
- // Load calendars when permission is granted
340
- useEffect(() => {
341
- if (permission?.granted) {
342
- loadSystemCalendars();
343
- }
344
- }, [permission, loadSystemCalendars]);
345
-
346
- return {
347
- systemCalendars,
348
- permission,
349
- isLoading,
350
- requestPermission,
351
- loadSystemCalendars,
352
- syncEventToCalendar,
353
- updateSyncedEvent,
354
- deleteSyncedEvent,
355
- };
356
- };
@@ -1,196 +0,0 @@
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
- }
@@ -1,92 +0,0 @@
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
-
@@ -1,205 +0,0 @@
1
- /**
2
- * Calendar Sync Service
3
- *
4
- * Handles synchronization with system calendar.
5
- *
6
- * SOLID: Single Responsibility - Only sync operations
7
- * DRY: Centralized sync logic
8
- * KISS: Simple sync interface
9
- */
10
-
11
- import * as Calendar from 'expo-calendar';
12
- import { Platform } from 'react-native';
13
- import { CalendarPermissions } from './CalendarPermissions';
14
- import type { CalendarEvent, SystemCalendar } from '../../domain/entities/CalendarEvent.entity';
15
-
16
- export class CalendarSync {
17
- /**
18
- * Sync event to system calendar
19
- */
20
- static async syncToSystemCalendar(
21
- event: CalendarEvent
22
- ): Promise<{
23
- success: boolean;
24
- eventId?: string;
25
- calendarId?: string;
26
- error?: string;
27
- }> {
28
- try {
29
- if (Platform.OS === 'web') {
30
- return { success: false, error: 'Calendar sync not supported on web' };
31
- }
32
-
33
- const permission = await CalendarPermissions.requestPermissions();
34
- if (!permission.granted) {
35
- return { success: false, error: 'Calendar permission not granted' };
36
- }
37
-
38
- const primaryCal = await this.getPrimaryCalendar();
39
- if (!primaryCal) {
40
- return { success: false, error: 'No writable calendar found' };
41
- }
42
-
43
- const eventData = this.buildSystemEventData(event);
44
- const systemEventId = await Calendar.createEventAsync(primaryCal.id, eventData);
45
-
46
- return {
47
- success: true,
48
- eventId: systemEventId,
49
- calendarId: primaryCal.id
50
- };
51
- } catch (error) {
52
- return {
53
- success: false,
54
- error: error instanceof Error ? error.message : 'Failed to sync to calendar'
55
- };
56
- }
57
- }
58
-
59
- /**
60
- * Update system calendar event
61
- */
62
- static async updateSystemCalendarEvent(
63
- event: CalendarEvent
64
- ): Promise<{ success: boolean; error?: string }> {
65
- try {
66
- if (Platform.OS === 'web' || !event.systemCalendar) {
67
- return { success: false, error: 'No system calendar data' };
68
- }
69
-
70
- const permission = await CalendarPermissions.requestPermissions();
71
- if (!permission.granted) {
72
- return { success: false, error: 'Calendar permission not granted' };
73
- }
74
-
75
- const eventData = this.buildSystemEventData(event);
76
- await Calendar.updateEventAsync(event.systemCalendar.eventId, eventData);
77
-
78
- return { success: true };
79
- } catch (error) {
80
- return {
81
- success: false,
82
- error: error instanceof Error ? error.message : 'Failed to update event'
83
- };
84
- }
85
- }
86
-
87
- /**
88
- * Remove event from system calendar
89
- */
90
- static async removeFromSystemCalendar(
91
- eventId: string
92
- ): Promise<{ success: boolean; error?: string }> {
93
- try {
94
- if (Platform.OS === 'web') {
95
- return { success: false, error: 'Calendar sync not supported on web' };
96
- }
97
-
98
- const permission = await CalendarPermissions.requestPermissions();
99
- if (!permission.granted) {
100
- return { success: false, error: 'Calendar permission not granted' };
101
- }
102
-
103
- await Calendar.deleteEventAsync(eventId);
104
- return { success: true };
105
- } catch (error) {
106
- return {
107
- success: false,
108
- error: error instanceof Error ? error.message : 'Failed to remove from calendar'
109
- };
110
- }
111
- }
112
-
113
- /**
114
- * Get primary writable calendar
115
- */
116
- static async getPrimaryCalendar(): Promise<SystemCalendar | null> {
117
- try {
118
- if (Platform.OS === 'web') {
119
- return null;
120
- }
121
-
122
- const calendars = await Calendar.getCalendarsAsync();
123
- const writableCalendars = calendars.filter((cal: Calendar.Calendar) =>
124
- cal.allowsModifications &&
125
- (cal.source.type === 'local' || cal.source.type === 'caldav')
126
- );
127
-
128
- // Prefer default calendar, fallback to first writable
129
- const primaryCal = writableCalendars.find((cal: Calendar.Calendar) => cal.isPrimary) || writableCalendars[0];
130
- if (!primaryCal) return null;
131
-
132
- return {
133
- id: primaryCal.id,
134
- title: primaryCal.title,
135
- color: primaryCal.color,
136
- allowsModifications: primaryCal.allowsModifications,
137
- source: primaryCal.source.name,
138
- isPrimary: primaryCal.isPrimary || false
139
- };
140
- } catch {
141
- return null;
142
- }
143
- }
144
-
145
- /**
146
- * Get all system calendars
147
- */
148
- static async getSystemCalendars(): Promise<SystemCalendar[]> {
149
- try {
150
- if (Platform.OS === 'web') {
151
- return [];
152
- }
153
-
154
- const calendars = await Calendar.getCalendarsAsync();
155
- return calendars.map((cal: Calendar.Calendar) => ({
156
- id: cal.id,
157
- title: cal.title,
158
- color: cal.color,
159
- allowsModifications: cal.allowsModifications,
160
- source: cal.source.name,
161
- isPrimary: cal.isPrimary || false
162
- }));
163
- } catch {
164
- return [];
165
- }
166
- }
167
-
168
- /**
169
- * Build system calendar event data
170
- */
171
- private static buildSystemEventData(event: CalendarEvent) {
172
- const [year, month, day] = event.date.split('-').map(Number);
173
- let startDate = new Date(year, month - 1, day);
174
- let endDate = new Date(startDate);
175
-
176
- // Set time if provided
177
- if (event.time) {
178
- const [hours, minutes] = event.time.split(':').map(Number);
179
- startDate.setHours(hours, minutes, 0, 0);
180
- endDate.setHours(hours, minutes, 0, 0);
181
- }
182
-
183
- // Set duration
184
- if (event.duration) {
185
- endDate.setMinutes(endDate.getMinutes() + event.duration);
186
- } else {
187
- endDate.setHours(endDate.getHours() + 1); // Default 1 hour
188
- }
189
-
190
- // Create reminders
191
- const alarms = event.reminders?.map(minutesBefore => ({
192
- relativeOffset: -minutesBefore
193
- }));
194
-
195
- return {
196
- title: event.title,
197
- startDate,
198
- endDate,
199
- notes: event.description,
200
- location: event.location,
201
- alarms,
202
- timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
203
- };
204
- }
205
- }