- Use occurrenceStart instead of startTime in getEventsInRange so recurring events show their actual occurrence date to the AI - Add lazy CalDAV sync in ChatService (syncOnce before first DB access) - Add CaldavService.sync() with internal config check (silent no-op) - Show German recurrence description (e.g. "Jede Woche") instead of generic "Wiederkehrend" in EventCardBase via formatRecurrenceRule() - Move RepeatType and REPEAT_TYPE_LABELS from editEvent to shared - Separate calendar overlay useFocusEffect from event loading
127 lines
3.2 KiB
TypeScript
127 lines
3.2 KiB
TypeScript
import { View, Text } from "react-native";
|
|
import { Feather } from "@expo/vector-icons";
|
|
import { ReactNode } from "react";
|
|
import { useThemeStore } from "../stores/ThemeStore";
|
|
import { CardBase } from "./CardBase";
|
|
import {
|
|
isMultiDayEvent,
|
|
formatDateWithWeekday,
|
|
formatDateWithWeekdayShort,
|
|
formatTime,
|
|
formatRecurrenceRule,
|
|
} from "@calchat/shared";
|
|
|
|
type EventCardBaseProps = {
|
|
className?: string;
|
|
title: string;
|
|
startTime: Date;
|
|
endTime: Date;
|
|
description?: string;
|
|
recurrenceRule?: string;
|
|
children?: ReactNode;
|
|
};
|
|
|
|
function formatDuration(start: Date, end: Date): string {
|
|
const startDate = new Date(start);
|
|
const endDate = new Date(end);
|
|
const diffMs = endDate.getTime() - startDate.getTime();
|
|
const diffMins = Math.round(diffMs / 60000);
|
|
|
|
if (diffMins < 60) {
|
|
return `${diffMins} min`;
|
|
}
|
|
|
|
const hours = Math.floor(diffMins / 60);
|
|
const mins = diffMins % 60;
|
|
|
|
if (mins === 0) {
|
|
return hours === 1 ? "1 Std" : `${hours} Std`;
|
|
}
|
|
|
|
return `${hours} Std ${mins} min`;
|
|
}
|
|
|
|
export const EventCardBase = ({
|
|
className,
|
|
title,
|
|
startTime,
|
|
endTime,
|
|
description,
|
|
recurrenceRule,
|
|
children,
|
|
}: EventCardBaseProps) => {
|
|
const { theme } = useThemeStore();
|
|
const multiDay = isMultiDayEvent(startTime, endTime);
|
|
|
|
return (
|
|
<CardBase title={title} className={className} borderWidth={2}>
|
|
{/* Date */}
|
|
<View className="flex-row items-center mb-1">
|
|
<Feather
|
|
name="calendar"
|
|
size={16}
|
|
color={theme.textPrimary}
|
|
style={{ marginRight: 8 }}
|
|
/>
|
|
{multiDay ? (
|
|
<Text style={{ color: theme.textPrimary }}>
|
|
{formatDateWithWeekdayShort(startTime)} →{" "}
|
|
{formatDateWithWeekday(endTime)}
|
|
</Text>
|
|
) : (
|
|
<Text style={{ color: theme.textPrimary }}>
|
|
{formatDateWithWeekday(startTime)}
|
|
</Text>
|
|
)}
|
|
</View>
|
|
|
|
{/* Time with duration */}
|
|
<View className="flex-row items-center mb-1">
|
|
<Feather
|
|
name="clock"
|
|
size={16}
|
|
color={theme.textPrimary}
|
|
style={{ marginRight: 8 }}
|
|
/>
|
|
{multiDay ? (
|
|
<Text style={{ color: theme.textPrimary }}>
|
|
{formatTime(startTime)} → {formatTime(endTime)}
|
|
</Text>
|
|
) : (
|
|
<Text style={{ color: theme.textPrimary }}>
|
|
{formatTime(startTime)} - {formatTime(endTime)} (
|
|
{formatDuration(startTime, endTime)})
|
|
</Text>
|
|
)}
|
|
</View>
|
|
|
|
{/* Recurring indicator */}
|
|
{recurrenceRule && (
|
|
<View className="flex-row items-center mb-1">
|
|
<Feather
|
|
name="repeat"
|
|
size={16}
|
|
color={theme.textPrimary}
|
|
style={{ marginRight: 8 }}
|
|
/>
|
|
<Text style={{ color: theme.textPrimary }}>
|
|
{formatRecurrenceRule(recurrenceRule)}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
|
|
{/* Description */}
|
|
{description && (
|
|
<Text style={{ color: theme.textPrimary }} className="text-sm mt-1">
|
|
{description}
|
|
</Text>
|
|
)}
|
|
|
|
{/* Action buttons slot */}
|
|
{children}
|
|
</CardBase>
|
|
);
|
|
};
|
|
|
|
export default EventCardBase;
|