import { View, Text, TextInput, Pressable, ActivityIndicator, } from "react-native"; import { Frequency, rrulestr } from "rrule"; import BaseBackground from "../components/BaseBackground"; import { useThemeStore } from "../stores/ThemeStore"; import { useCallback, useEffect, useState } from "react"; import { router, useLocalSearchParams } from "expo-router"; import Header, { HeaderButton } from "../components/Header"; import { DatePickerButton, TimePickerButton, } from "../components/DateTimePicker"; import { Ionicons } from "@expo/vector-icons"; import { ScrollableDropdown } from "../components/ScrollableDropdown"; import { useDropdownPosition } from "../hooks/useDropdownPosition"; import { EventService, ChatService } from "../services"; import { buildRRule, CreateEventDTO } from "@calchat/shared"; import { useChatStore } from "../stores"; type EditEventTextFieldProps = { titel: string; text?: string; focused?: boolean; className?: string; multiline?: boolean; onValueChange?: (text: string) => void; }; const EditEventTextField = (props: EditEventTextFieldProps) => { const { theme } = useThemeStore(); const [focused, setFocused] = useState(props.focused ?? false); return ( {props.titel} setFocused(true)} onBlur={() => setFocused(false)} /> ); }; type PickerRowProps = { title: string; showLabels?: boolean; dateValue: Date; onDateChange: (date: Date) => void; onTimeChange: (date: Date) => void; }; const PickerRow = ({ showLabels, dateValue, title, onDateChange, onTimeChange, }: PickerRowProps) => { const { theme } = useThemeStore(); return ( {title} ); }; type RepeatType = "Tag" | "Woche" | "Monat" | "Jahr"; const REPEAT_TYPE_LABELS: Record = { Tag: "Tage", Woche: "Wochen", Monat: "Monate", Jahr: "Jahre", }; type RepeatPressableProps = { focused: boolean; repeatType: RepeatType; setRepeatType: (repeatType: RepeatType) => void; }; const RepeatPressable = ({ focused, repeatType, setRepeatType, }: RepeatPressableProps) => { const { theme } = useThemeStore(); return ( setRepeatType(repeatType)} > {repeatType} ); }; type RepeatSelectorProps = { repeatCount: number; onRepeatCountChange: (count: number) => void; repeatType: RepeatType; onRepeatTypeChange: (type: RepeatType) => void; }; // Static data for repeat count dropdown (1-120) const REPEAT_COUNT_DATA = Array.from({ length: 120 }, (_, i) => i + 1); const RepeatSelector = ({ repeatCount, onRepeatCountChange, repeatType, onRepeatTypeChange, }: RepeatSelectorProps) => { const { theme } = useThemeStore(); const dropdown = useDropdownPosition(2); const handleSelectCount = useCallback( (count: number) => { onRepeatCountChange(count); dropdown.close(); }, [onRepeatCountChange, dropdown], ); const typeLabel = REPEAT_TYPE_LABELS[repeatType]; return ( {/* Repeat Type Selection */} {/* Repeat Count Selection */} Alle{" "} {repeatCount} {" "} {typeLabel} {/* Count Dropdown */} String(n)} renderItem={(n, theme) => ( {n} )} onSelect={handleSelectCount} heightRatio={0.4} initialScrollIndex={repeatCount - 1} /> ); }; type EditEventHeaderProps = { id?: string; mode?: "calendar" | "chat"; }; const EditEventHeader = ({ id, mode }: EditEventHeaderProps) => { const getTitle = () => { if (mode === "chat") return "Edit Proposal"; return id ? "Edit Meeting" : "New Meeting"; }; return (
{getTitle()}
); }; type EditEventParams = { id?: string; date?: string; mode?: "calendar" | "chat"; eventData?: string; proposalContext?: string; }; type ProposalContext = { messageId: string; proposalId: string; conversationId: string; }; const EditEventScreen = () => { const { id, date, mode, eventData, proposalContext } = useLocalSearchParams(); const { theme } = useThemeStore(); const updateMessage = useChatStore((state) => state.updateMessage); // Only show loading if we need to fetch from API (calendar mode with id) const [isLoading, setIsLoading] = useState( mode !== "chat" && !!id && !eventData, ); // Initialize dates from URL parameter or use current time const initialDate = date ? new Date(date) : new Date(); const initialEndDate = new Date(initialDate.getTime() + 60 * 60 * 1000); const [repeatVisible, setRepeatVisible] = useState(false); const [repeatCount, setRepeatCount] = useState(1); const [repeatType, setRepeatType] = useState("Tag"); const [startDate, setStartDate] = useState(initialDate); const [endDate, setEndDate] = useState(initialEndDate); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); // Helper to populate form from event data const populateFormFromEvent = useCallback((event: CreateEventDTO) => { setStartDate(new Date(event.startTime)); setEndDate(new Date(event.endTime)); setTitle(event.title); if (event.description) { setDescription(event.description); } if (event.recurrenceRule) { setRepeatVisible(true); const rrule = rrulestr(event.recurrenceRule); if (rrule.options.interval) { setRepeatCount(rrule.options.interval); } switch (rrule.options.freq) { case Frequency.DAILY: setRepeatType("Tag"); break; case Frequency.WEEKLY: setRepeatType("Woche"); break; case Frequency.MONTHLY: setRepeatType("Monat"); break; case Frequency.YEARLY: setRepeatType("Jahr"); break; } } }, []); // Load event data based on mode useEffect(() => { // Chat mode: load from eventData JSON parameter if (mode === "chat" && eventData) { try { const event = JSON.parse(eventData) as CreateEventDTO; populateFormFromEvent(event); } catch (error) { console.error("Failed to parse eventData:", error); } return; } // Calendar mode with id: fetch from API if (id && !eventData) { const fetchEvent = async () => { try { const event = await EventService.getById(id); populateFormFromEvent({ title: event.title, description: event.description, startTime: event.startTime, endTime: event.endTime, recurrenceRule: event.recurrenceRule, }); } catch (error) { console.error("Failed to load event: ", error); } finally { setIsLoading(false); } }; fetchEvent(); } }, [id, mode, eventData, populateFormFromEvent]); if (isLoading) { return ( ); } const handleStartDateChange = (date: Date) => { // Keep the time from startDate, update the date part const newStart = new Date(startDate); newStart.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); setStartDate(newStart); // If end date is before new start date, adjust it if (endDate < newStart) { const newEnd = new Date(newStart); newEnd.setHours(newStart.getHours() + 1); setEndDate(newEnd); } }; const handleStartTimeChange = (date: Date) => { // Keep the date from startDate, update the time part const newStart = new Date(startDate); newStart.setHours(date.getHours(), date.getMinutes(), 0, 0); setStartDate(newStart); // If end time is before new start time on the same day, adjust it if (endDate <= newStart) { const newEnd = new Date(newStart); newEnd.setHours(newStart.getHours() + 1); setEndDate(newEnd); } }; const handleEndDateChange = (date: Date) => { // Keep the time from endDate, update the date part const newEnd = new Date(endDate); newEnd.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); setEndDate(newEnd); }; const handleEndTimeChange = (date: Date) => { // Keep the date from endDate, update the time part const newEnd = new Date(endDate); newEnd.setHours(date.getHours(), date.getMinutes(), 0, 0); setEndDate(newEnd); }; const handleSave = async () => { const eventObject: CreateEventDTO = { title, description: description === "" ? undefined : description, startTime: startDate, endTime: endDate, recurrenceRule: repeatVisible ? buildRRule(repeatType, repeatCount) : undefined, }; // Chat mode: update proposal on server and sync response to local store if (mode === "chat" && proposalContext) { try { const context = JSON.parse(proposalContext) as ProposalContext; // Persist to server - returns updated message with recalculated conflictingEvents const updatedMessage = await ChatService.updateProposalEvent( context.messageId, context.proposalId, eventObject, ); // Update local ChatStore with server response (includes updated conflicts) if (updatedMessage?.proposedChanges) { updateMessage(context.messageId, { proposedChanges: updatedMessage.proposedChanges, }); } router.back(); } catch (error) { console.error("Failed to update proposal:", error); } return; } // Calendar mode: call API try { if (id) { await EventService.update(id, eventObject); } else { await EventService.create(eventObject); } router.back(); } catch (error) { console.error("Creating/Updating event failed!", error); } }; const getButtonText = () => { if (mode === "chat") { return "Fertig"; } return id ? "Aktualisiere Termin" : "Erstelle neuen Termin"; }; return ( {/* Date and Time */} {/* TODO: Reminder */} {/* Notes */} {/* Repeat Toggle Button */} setRepeatVisible(!repeatVisible)} > Wiederholen {/* Repeat Selector (shown when toggle is active) */} {repeatVisible && ( )} {/* Send new or updated Event */} {mode !== "chat" && ( )} {getButtonText()} ); }; export default EditEventScreen;