Use a two-phase delete flow for conversation list items. Instead of immediately removing the deleted conversation from the array and relying only on transition-group leave hooks, mark the target conversation as pending deletion first so it can play the left-slide animation, then remove it after the animation completes. Also keep the list container mounted during delete mode so empty/loading branches do not interrupt the exit transition. Validation: npm run build --silent 2>&1 | tail -n 20
85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
import { defineStore } from 'pinia';
|
|
|
|
export interface ConversationSummary {
|
|
id: string;
|
|
title: string;
|
|
updated_at: string | number;
|
|
total_messages?: number;
|
|
total_tools?: number;
|
|
}
|
|
|
|
interface ConversationState {
|
|
conversations: ConversationSummary[];
|
|
searchResults: ConversationSummary[];
|
|
conversationInsertAnimations: Record<string, 'create' | 'duplicate' | 'duplicateSource'>;
|
|
conversationListAnimationMode: 'idle' | 'create' | 'duplicate' | 'delete';
|
|
pendingDeletingConversationIds: string[];
|
|
conversationsLoading: boolean;
|
|
hasMoreConversations: boolean;
|
|
loadingMoreConversations: boolean;
|
|
currentConversationId: string | null;
|
|
currentConversationTitle: string;
|
|
searchQuery: string;
|
|
searchTimer: ReturnType<typeof setTimeout> | null;
|
|
searchActive: boolean;
|
|
searchInProgress: boolean;
|
|
searchMoreAvailable: boolean;
|
|
searchOffset: number;
|
|
searchTotal: number;
|
|
conversationsOffset: number;
|
|
conversationsLimit: number;
|
|
runningWorkspaceTasks: any[];
|
|
acknowledgedCompletedTaskIds: string[];
|
|
}
|
|
|
|
export const useConversationStore = defineStore('conversation', {
|
|
state: (): ConversationState => ({
|
|
conversations: [],
|
|
searchResults: [],
|
|
conversationInsertAnimations: {},
|
|
conversationListAnimationMode: 'idle',
|
|
pendingDeletingConversationIds: [],
|
|
conversationsLoading: false,
|
|
hasMoreConversations: false,
|
|
loadingMoreConversations: false,
|
|
currentConversationId: null,
|
|
currentConversationTitle: '',
|
|
searchQuery: '',
|
|
searchTimer: null,
|
|
searchActive: false,
|
|
searchInProgress: false,
|
|
searchMoreAvailable: false,
|
|
searchOffset: 0,
|
|
searchTotal: 0,
|
|
conversationsOffset: 0,
|
|
conversationsLimit: 20,
|
|
runningWorkspaceTasks: [],
|
|
acknowledgedCompletedTaskIds: []
|
|
}),
|
|
actions: {
|
|
resetConversations() {
|
|
this.conversations = [];
|
|
this.searchResults = [];
|
|
this.conversationInsertAnimations = {};
|
|
this.conversationListAnimationMode = 'idle';
|
|
this.pendingDeletingConversationIds = [];
|
|
this.searchActive = false;
|
|
this.searchInProgress = false;
|
|
this.searchMoreAvailable = false;
|
|
this.searchOffset = 0;
|
|
this.searchTotal = 0;
|
|
this.hasMoreConversations = false;
|
|
this.loadingMoreConversations = false;
|
|
this.conversationsOffset = 0;
|
|
this.runningWorkspaceTasks = [];
|
|
this.acknowledgedCompletedTaskIds = [];
|
|
},
|
|
cancelSearchTimer() {
|
|
if (this.searchTimer) {
|
|
clearTimeout(this.searchTimer);
|
|
this.searchTimer = null;
|
|
}
|
|
}
|
|
}
|
|
});
|