1- import React , { useState , useEffect } from "react" ;
1+ import React , { useState , useEffect , useMemo } from "react" ;
22import { ChatInterface } from "./chat/ChatInterface" ;
3- import { ConversationTabs } from ". /chat/ConversationTabs" ;
3+ import { ConversationTabs } from "@/components /chat/ConversationTabs" ;
44import { useConversationStore } from "@/stores/ConversationStore" ;
5- import { useLayoutStore } from "@/stores/layoutStore" ;
6- import { sessionManager } from "@/services/sessionManager" ;
7- import { sessionLoader } from "@/services/sessionLoader" ;
85import type { Conversation } from "@/types/chat" ;
9- import { invoke } from "@tauri-apps/api/core" ;
10- import { DebugInfo } from "./common/DebugInfo" ;
116
12- export const ChatView : React . FC = ( ) => {
13- const [ selectedConversation , setSelectedConversation ] =
14- useState < Conversation | null > ( null ) ;
15- const [ searchQueries , setSearchQueries ] = useState ( {
16- all : "" ,
17- favorites : "" ,
18- sessions : ""
19- } ) ;
20- const [ historyConversations , setHistoryConversations ] = useState <
21- Conversation [ ]
22- > ( [ ] ) ;
23- const [ favoriteStatuses , setFavoriteStatuses ] = useState <
24- Record < string , boolean >
25- > ( { } ) ;
7+ interface ChatViewProps {
8+ selectedConversation ?: Conversation | null ;
9+ showChatTabs ?: boolean ;
10+ }
2611
12+ export const ChatView : React . FC < ChatViewProps > = ( { selectedConversation, showChatTabs = false } ) => {
2713 const {
2814 currentConversationId,
2915 conversations : activeConversations ,
30- createConversationWithLatestSession,
31- selectHistoryConversation,
3216 deleteConversation,
3317 setCurrentConversation,
18+ selectHistoryConversation,
3419 pendingNewConversation,
20+ toggleFavorite,
3521 } = useConversationStore ( ) ;
36- const { showSessionList, conversationListTab } = useLayoutStore ( ) ;
3722
38- // Don't auto-switch tabs to prevent unwanted tab changes when favoriting/deleting conversations
39- // Users can manually switch tabs as needed
23+ const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
24+ const [ internalSelectedConversation , setInternalSelectedConversation ] = useState < Conversation | null > ( null ) ;
4025
41- // Clear selected conversation when starting a new conversation
42- useEffect ( ( ) => {
43- if ( pendingNewConversation ) {
44- setSelectedConversation ( null ) ;
45- }
46- } , [ pendingNewConversation ] ) ;
26+ // Generate favorite statuses from the persisted store data
27+ const favoriteStatuses = useMemo ( ( ) => {
28+ const statuses : Record < string , boolean > = { } ;
29+ activeConversations . forEach ( conv => {
30+ statuses [ conv . id ] = conv . isFavorite || false ;
31+ } ) ;
32+ return statuses ;
33+ } , [ activeConversations ] ) ;
4734
48- // Also clear when currentConversationId changes to a new codex-event format (from toolbar button)
49- useEffect ( ( ) => {
50- if ( currentConversationId && currentConversationId . startsWith ( 'codex-event-' ) ) {
51- // Check if this is a new conversation (no messages)
52- const currentConv = activeConversations . find ( conv => conv . id === currentConversationId ) ;
53- if ( currentConv && currentConv . messages . length === 0 ) {
54- setSelectedConversation ( null ) ;
55- }
56- }
57- } , [ currentConversationId , activeConversations ] ) ;
58-
59- const loadHistory = async ( ) => {
60- try {
61- const history = await sessionLoader . loadSessionsFromDisk ( ) ;
62- setHistoryConversations ( history ) ;
63-
64- const statuses : Record < string , boolean > = { } ;
65- for ( const conv of history ) {
66- statuses [ conv . id ] = await sessionLoader . isConversationFavorited (
67- conv . id ,
68- ) ;
69- }
70- setFavoriteStatuses ( statuses ) ;
71- } catch ( error ) {
72- console . error ( "Failed to load history conversations:" , error ) ;
73- }
74- } ;
75-
76- useEffect ( ( ) => {
77- loadHistory ( ) ;
78- } , [ ] ) ;
79-
80- // Handle creating new conversation with latest session
81- const handleCreateNewConversation = async ( ) => {
82- try {
83- await createConversationWithLatestSession ( ) ;
84- } catch ( error ) {
85- console . error ( "Failed to create new conversation:" , error ) ;
86- }
87- } ;
88-
89- // Handle conversation selection from history
9035 const handleConversationSelect = ( conversation : Conversation ) => {
91- // Always create a new object to force re-render even if same conversation
9236 const conversationCopy = { ...conversation } ;
93-
94- // Store the selected conversation data
95- setSelectedConversation ( conversationCopy ) ;
96-
97- // Use the new method to select history conversation with full data
37+ setInternalSelectedConversation ( conversationCopy ) ;
9838 console . log ( "🔄 ChatView: Calling selectHistoryConversation" , conversation . id ) ;
9939 selectHistoryConversation ( conversationCopy ) ;
10040 } ;
10141
10242 const handleSelectSession = ( sessionId : string ) => {
103- // Set the current session ID for communication
10443 setCurrentConversation ( sessionId ) ;
10544 } ;
10645
@@ -110,23 +49,7 @@ export const ChatView: React.FC = () => {
11049 ) => {
11150 e . stopPropagation ( ) ;
11251 try {
113- const conversation = historyConversations . find (
114- ( c ) => c . id === conversationId ,
115- ) ;
116- if ( conversation && conversation . filePath ) {
117- await invoke ( "delete_session_file" , {
118- filePath : conversation . filePath ,
119- } ) ;
120- }
12152 deleteConversation ( conversationId ) ;
122- setHistoryConversations ( ( prev ) =>
123- prev . filter ( ( c ) => c . id !== conversationId ) ,
124- ) ;
125- setFavoriteStatuses ( ( prev ) => {
126- const newStatus = { ...prev } ;
127- delete newStatus [ conversationId ] ;
128- return newStatus ;
129- } ) ;
13053 } catch ( error ) {
13154 console . error ( "Failed to delete conversation and session file:" , error ) ;
13255 }
@@ -138,89 +61,57 @@ export const ChatView: React.FC = () => {
13861 ) => {
13962 e . stopPropagation ( ) ;
14063 try {
141- await sessionLoader . toggleFavorite ( conversationId ) ;
142-
143- // Update local favorite status
144- setFavoriteStatuses ( ( prev ) => ( {
145- ...prev ,
146- [ conversationId ] : ! prev [ conversationId ] ,
147- } ) ) ;
64+ // Use the store's toggleFavorite method directly for persistence
65+ toggleFavorite ( conversationId ) ;
14866 } catch ( error ) {
14967 console . error ( "Failed to toggle favorite:" , error ) ;
15068 }
15169 } ;
15270
153- const handleKillSession = async ( sessionId : string ) => {
154- console . log ( `💀 ChatView: Killing session ${ sessionId } ` ) ;
155- try {
156- // First check if it's a timestamp format session
157- const isTimestampFormat = sessionId . startsWith ( 'codex-event-' ) && sessionId . includes ( '-' ) &&
158- / \d { 13 } - [ a - z 0 - 9 ] + $ / . test ( sessionId . replace ( 'codex-event-' , '' ) ) ;
159-
160- if ( ! isTimestampFormat ) {
161- console . log ( `💀 Ignoring UUID session kill: ${ sessionId } ` ) ;
162- return ;
163- }
164-
165- // Extract the raw session ID for backend process management
166- const rawSessionId = sessionId . replace ( 'codex-event-' , '' ) ;
167- console . log ( `🔄 Extracting raw session ID: ${ rawSessionId } ` ) ;
71+ // Clear selected conversation when starting a new conversation
72+ useEffect ( ( ) => {
73+ if ( pendingNewConversation ) {
74+ setInternalSelectedConversation ( null ) ;
75+ }
76+ } , [ pendingNewConversation ] ) ;
16877
169- await sessionManager . stopSession ( rawSessionId ) ;
170- console . log ( `✅ Session stopped: ${ sessionId } ` ) ;
171-
172- // Remove the conversation from the store to update UI
173- deleteConversation ( sessionId ) ;
174- } catch ( error ) {
175- console . warn ( 'Failed to kill session (session may have already been cleaned up):' , error ) ;
176- // Don't throw error - session might have been cleaned up by hot reload or other reasons
78+ // Also clear when currentConversationId changes to a new codex-event format (from toolbar button)
79+ useEffect ( ( ) => {
80+ if ( currentConversationId && currentConversationId . startsWith ( 'codex-event-' ) ) {
81+ // Check if this is a new conversation (no messages)
82+ const currentConv = activeConversations . find ( conv => conv . id === currentConversationId ) ;
83+ if ( currentConv && currentConv . messages . length === 0 ) {
84+ setInternalSelectedConversation ( null ) ;
85+ }
17786 }
178- } ;
87+ } , [ currentConversationId , activeConversations ] ) ;
17988
89+ // Use either the passed selectedConversation or internal one
90+ const displayedConversation = selectedConversation || internalSelectedConversation ;
91+
92+ if ( showChatTabs ) {
93+ return (
94+ < ConversationTabs
95+ favoriteStatuses = { favoriteStatuses }
96+ activeConversations = { activeConversations }
97+ currentConversationId = { currentConversationId }
98+ activeSessionId = { currentConversationId || '' }
99+ searchQuery = { searchQuery }
100+ onSearchChange = { setSearchQuery }
101+ onSelectConversation = { handleConversationSelect }
102+ onToggleFavorite = { handleToggleFavorite }
103+ onDeleteConversation = { handleDeleteConversation }
104+ onSelectSession = { handleSelectSession }
105+ />
106+ ) ;
107+ }
180108
181109 return (
182- < div className = "flex h-full min-h-0" >
183- { showSessionList && (
184- < div className = "w-64 border-r h-full overflow-y-auto flex-shrink-0" >
185- < div className = "flex flex-col h-full bg-background" >
186-
187- < DebugInfo
188- conversationListTab = { conversationListTab }
189- currentConversationId = { currentConversationId }
190- historyConversationsCount = { historyConversations . length }
191- activeConversationsCount = { activeConversations . length }
192- searchQueries = { searchQueries }
193- />
194-
195- < ConversationTabs
196- historyConversations = { historyConversations }
197- favoriteStatuses = { favoriteStatuses }
198- activeConversations = { activeConversations }
199- currentConversationId = { currentConversationId }
200- activeSessionId = { currentConversationId || '' }
201- searchQueries = { searchQueries }
202- onSearchChange = { setSearchQueries }
203- onSelectConversation = { handleConversationSelect }
204- onToggleFavorite = { handleToggleFavorite }
205- onDeleteConversation = { handleDeleteConversation }
206- onSelectSession = { handleSelectSession }
207- onKillSession = { handleKillSession }
208- onRefreshConversations = { loadHistory }
209- />
210- </ div >
211- </ div >
212- ) }
213- < div className = "flex-1 min-h-0 h-full min-w-0" >
214- < ChatInterface
215- sessionId = { currentConversationId || '' }
216- activeSessionId = { currentConversationId || '' }
217- onCreateSession = { handleCreateNewConversation }
218- onSelectSession = { handleSelectSession }
219- onCloseSession = { deleteConversation }
220- isSessionListVisible = { false }
221- selectedConversation = { selectedConversation }
222- />
223- </ div >
110+ < div className = "flex-1 min-h-0 h-full min-w-0" >
111+ < ChatInterface
112+ sessionId = { currentConversationId || '' }
113+ selectedConversation = { displayedConversation }
114+ />
224115 </ div >
225116 ) ;
226117} ;
0 commit comments