refactor: improve AI event handling and conflict display in chat

- AI fetches events on-demand via callbacks for better efficiency
- Add conflict detection with warning display when proposing overlapping events
- Improve event search and display in chat interface
- Load full chat history for display while limiting AI context
This commit is contained in:
2026-02-02 22:44:08 +01:00
parent 387bb2d1ee
commit 1092ff2648
19 changed files with 367 additions and 119 deletions

View File

@@ -22,9 +22,6 @@ import { EventService, ChatService } from "../services";
import { buildRRule, CreateEventDTO } from "@calchat/shared";
import { useChatStore } from "../stores";
// Direct store access for getting current state in callbacks
const getChatStoreState = () => useChatStore.getState();
type EditEventTextFieldProps = {
titel: string;
text?: string;
@@ -443,31 +440,25 @@ const EditEventScreen = () => {
: undefined,
};
// Chat mode: update proposal locally and on server
// Chat mode: update proposal on server and sync response to local store
if (mode === "chat" && proposalContext) {
try {
const context = JSON.parse(proposalContext) as ProposalContext;
// Update locally in ChatStore
const currentMessages = getChatStoreState().messages;
const message = currentMessages.find((m) => m.id === context.messageId);
if (message?.proposedChanges) {
const updatedProposals = message.proposedChanges.map((p) =>
p.id === context.proposalId ? { ...p, event: eventObject } : p,
);
updateMessage(context.messageId, {
proposedChanges: updatedProposals,
});
}
// Persist to server
await ChatService.updateProposalEvent(
// 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);

View File

@@ -1,6 +1,6 @@
import { View, Text, Pressable } from "react-native";
import { Feather } from "@expo/vector-icons";
import { ProposedEventChange, formatDate } from "@calchat/shared";
import { Feather, Ionicons } from "@expo/vector-icons";
import { ProposedEventChange, formatDate, formatTime } from "@calchat/shared";
import { rrulestr } from "rrule";
import { useThemeStore } from "../stores/ThemeStore";
import { EventCardBase } from "./EventCardBase";
@@ -143,6 +143,29 @@ export const ProposedEventCard = ({
</Text>
</View>
)}
{/* Show conflicting events warning */}
{proposedChange.conflictingEvents &&
proposedChange.conflictingEvents.length > 0 && (
<View className="mb-2">
{proposedChange.conflictingEvents.map((conflict, index) => (
<View key={index} className="flex-row items-center mt-1">
<Ionicons
name="alert-circle"
size={16}
color={theme.rejectButton}
style={{ marginRight: 8 }}
/>
<Text
style={{ color: theme.rejectButton }}
className="text-sm flex-1"
>
Konflikt: {conflict.title} ({formatTime(conflict.startTime)}{" "}
- {formatTime(conflict.endTime)})
</Text>
</View>
))}
</View>
)}
<ActionButtons
isDisabled={isDisabled}
respondedAction={proposedChange.respondedAction}