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 (
);
};
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;