@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,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
+ }
@@ -0,0 +1,205 @@
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
+ }
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Calendar Store (Zustand)
3
+ *
4
+ * Global state management for calendar functionality.
5
+ * Manages calendar view state, selected date, and events.
6
+ *
7
+ * Design Philosophy:
8
+ * - Zustand for lightweight state
9
+ * - AsyncStorage for persistence
10
+ * - Generic event handling
11
+ * - Timezone-aware via CalendarService
12
+ */
13
+
14
+ import { create } from 'zustand';
15
+ import { persist, createJSONStorage } from 'zustand/middleware';
16
+ import AsyncStorage from '@react-native-async-storage/async-storage';
17
+ import type { CalendarEvent, CreateCalendarEventRequest, UpdateCalendarEventRequest } from '../../domain/entities/CalendarEvent.entity';
18
+ import { CalendarService } from '../services/CalendarService';
19
+
20
+ /**
21
+ * Calendar view mode
22
+ */
23
+ export type CalendarViewMode = 'month' | 'week' | 'day' | 'list';
24
+
25
+ /**
26
+ * Calendar state interface
27
+ */
28
+ interface CalendarState {
29
+ // State
30
+ events: CalendarEvent[];
31
+ selectedDate: Date;
32
+ currentMonth: Date;
33
+ viewMode: CalendarViewMode;
34
+ isLoading: boolean;
35
+ error: string | null;
36
+
37
+ // Actions
38
+ actions: {
39
+ // Event CRUD
40
+ loadEvents: () => Promise<void>;
41
+ addEvent: (request: CreateCalendarEventRequest) => Promise<void>;
42
+ updateEvent: (request: UpdateCalendarEventRequest) => Promise<void>;
43
+ deleteEvent: (id: string) => Promise<void>;
44
+ completeEvent: (id: string) => Promise<void>;
45
+ uncompleteEvent: (id: string) => Promise<void>;
46
+
47
+ // Navigation
48
+ setSelectedDate: (date: Date) => void;
49
+ goToToday: () => void;
50
+ navigateMonth: (direction: 'prev' | 'next') => void;
51
+ navigateWeek: (direction: 'prev' | 'next') => void;
52
+ setCurrentMonth: (date: Date) => void;
53
+
54
+ // View mode
55
+ setViewMode: (mode: CalendarViewMode) => void;
56
+
57
+ // Utilities
58
+ getEventsForDate: (date: Date) => CalendarEvent[];
59
+ getEventsForMonth: (year: number, month: number) => CalendarEvent[];
60
+ clearError: () => void;
61
+ clearAllEvents: () => Promise<void>;
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Storage key for calendar events
67
+ */
68
+ const STORAGE_KEY = 'calendar_events';
69
+
70
+ /**
71
+ * Generate unique ID for events
72
+ */
73
+ const generateId = (): string => {
74
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
75
+ };
76
+
77
+ /**
78
+ * Calendar Store
79
+ */
80
+ export const useCalendarStore = create<CalendarState>()(
81
+ persist(
82
+ (set, get) => ({
83
+ // Initial State
84
+ events: [],
85
+ selectedDate: new Date(),
86
+ currentMonth: new Date(),
87
+ viewMode: 'month',
88
+ isLoading: false,
89
+ error: null,
90
+
91
+ // Actions
92
+ actions: {
93
+ /**
94
+ * Load events from storage
95
+ */
96
+ loadEvents: async () => {
97
+ set({ isLoading: true, error: null });
98
+ try {
99
+ const stored = await AsyncStorage.getItem(STORAGE_KEY);
100
+ if (stored) {
101
+ const events = JSON.parse(stored);
102
+ // Restore Date objects
103
+ events.forEach((event: CalendarEvent) => {
104
+ event.createdAt = new Date(event.createdAt);
105
+ event.updatedAt = new Date(event.updatedAt);
106
+ });
107
+ set({ events, isLoading: false });
108
+ } else {
109
+ set({ isLoading: false });
110
+ }
111
+ } catch (error) {
112
+ set({
113
+ error: error instanceof Error ? error.message : 'Failed to load events',
114
+ isLoading: false
115
+ });
116
+ }
117
+ },
118
+
119
+ /**
120
+ * Add a new event
121
+ */
122
+ addEvent: async (request: CreateCalendarEventRequest) => {
123
+ set({ isLoading: true, error: null });
124
+ try {
125
+ const newEvent: CalendarEvent = {
126
+ id: generateId(),
127
+ ...request,
128
+ isCompleted: false,
129
+ createdAt: new Date(),
130
+ updatedAt: new Date(),
131
+ };
132
+
133
+ const events = [...get().events, newEvent];
134
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
135
+ set({ events, isLoading: false });
136
+ } catch (error) {
137
+ set({
138
+ error: error instanceof Error ? error.message : 'Failed to add event',
139
+ isLoading: false
140
+ });
141
+ }
142
+ },
143
+
144
+ /**
145
+ * Update an existing event
146
+ */
147
+ updateEvent: async (request: UpdateCalendarEventRequest) => {
148
+ set({ isLoading: true, error: null });
149
+ try {
150
+ const events = get().events.map(event => {
151
+ if (event.id === request.id) {
152
+ return {
153
+ ...event,
154
+ ...request,
155
+ updatedAt: new Date(),
156
+ };
157
+ }
158
+ return event;
159
+ });
160
+
161
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
162
+ set({ events, isLoading: false });
163
+ } catch (error) {
164
+ set({
165
+ error: error instanceof Error ? error.message : 'Failed to update event',
166
+ isLoading: false
167
+ });
168
+ }
169
+ },
170
+
171
+ /**
172
+ * Delete an event
173
+ */
174
+ deleteEvent: async (id: string) => {
175
+ set({ isLoading: true, error: null });
176
+ try {
177
+ const events = get().events.filter(event => event.id !== id);
178
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(events));
179
+ set({ events, isLoading: false });
180
+ } catch (error) {
181
+ set({
182
+ error: error instanceof Error ? error.message : 'Failed to delete event',
183
+ isLoading: false
184
+ });
185
+ }
186
+ },
187
+
188
+ /**
189
+ * Mark event as completed
190
+ */
191
+ completeEvent: async (id: string) => {
192
+ await get().actions.updateEvent({ id, isCompleted: true });
193
+ },
194
+
195
+ /**
196
+ * Mark event as incomplete
197
+ */
198
+ uncompleteEvent: async (id: string) => {
199
+ await get().actions.updateEvent({ id, isCompleted: false });
200
+ },
201
+
202
+ /**
203
+ * Set selected date
204
+ */
205
+ setSelectedDate: (date: Date) => {
206
+ set({ selectedDate: date });
207
+ },
208
+
209
+ /**
210
+ * Go to today's date
211
+ */
212
+ goToToday: () => {
213
+ const today = new Date();
214
+ set({
215
+ selectedDate: today,
216
+ currentMonth: today,
217
+ });
218
+ },
219
+
220
+ /**
221
+ * Navigate to previous/next month
222
+ */
223
+ navigateMonth: (direction: 'prev' | 'next') => {
224
+ const currentMonth = get().currentMonth;
225
+ const newMonth = direction === 'prev'
226
+ ? CalendarService.getPreviousMonth(currentMonth)
227
+ : CalendarService.getNextMonth(currentMonth);
228
+
229
+ set({ currentMonth: newMonth });
230
+ },
231
+
232
+ /**
233
+ * Navigate to previous/next week
234
+ */
235
+ navigateWeek: (direction: 'prev' | 'next') => {
236
+ const selectedDate = get().selectedDate;
237
+ const newDate = direction === 'prev'
238
+ ? CalendarService.getPreviousWeek(selectedDate)
239
+ : CalendarService.getNextWeek(selectedDate);
240
+
241
+ set({ selectedDate: newDate });
242
+ },
243
+
244
+ /**
245
+ * Set current month directly
246
+ */
247
+ setCurrentMonth: (date: Date) => {
248
+ set({ currentMonth: date });
249
+ },
250
+
251
+ /**
252
+ * Set view mode
253
+ */
254
+ setViewMode: (mode: CalendarViewMode) => {
255
+ set({ viewMode: mode });
256
+ },
257
+
258
+ /**
259
+ * Get events for a specific date
260
+ */
261
+ getEventsForDate: (date: Date) => {
262
+ const events = get().events;
263
+ return CalendarService.getEventsForDate(date, events);
264
+ },
265
+
266
+ /**
267
+ * Get events for a specific month
268
+ */
269
+ getEventsForMonth: (year: number, month: number) => {
270
+ const events = get().events;
271
+ const firstDay = new Date(year, month, 1);
272
+ const lastDay = new Date(year, month + 1, 0);
273
+ return CalendarService.getEventsInRange(firstDay, lastDay, events);
274
+ },
275
+
276
+ /**
277
+ * Clear error state
278
+ */
279
+ clearError: () => {
280
+ set({ error: null });
281
+ },
282
+
283
+ /**
284
+ * Clear all events (for testing/reset)
285
+ */
286
+ clearAllEvents: async () => {
287
+ set({ isLoading: true, error: null });
288
+ try {
289
+ await AsyncStorage.removeItem(STORAGE_KEY);
290
+ set({ events: [], isLoading: false });
291
+ } catch (error) {
292
+ set({
293
+ error: error instanceof Error ? error.message : 'Failed to clear events',
294
+ isLoading: false
295
+ });
296
+ }
297
+ },
298
+ },
299
+ }),
300
+ {
301
+ name: 'calendar-storage',
302
+ storage: createJSONStorage(() => AsyncStorage),
303
+ // Only persist events, not UI state
304
+ partialize: (state) => ({ events: state.events }),
305
+ }
306
+ )
307
+ );