@@ -25,6 +25,8 @@ struct GUI: ReducerProtocol {
2525 case suggestionWidget( WidgetFeature . Action )
2626 }
2727
28+ @Dependency ( \. chatTabPool) var chatTabPool : ChatTabPool
29+
2830 var body : some ReducerProtocol < State , Action > {
2931 Scope ( state: \. suggestionWidgetState, action: / Action. suggestionWidget) {
3032 WidgetFeature ( )
@@ -37,16 +39,24 @@ struct GUI: ReducerProtocol {
3739 Reduce { _, action in
3840 switch action {
3941 case let . createNewTapButtonClicked( kind) :
40- guard let builder = kind? . builder else {
41- let chatTap = ChatGPTChatTab ( )
42- return . run { send in
43- await send ( . appendAndSelectTab( chatTap) )
42+ return . run { send in
43+ if let ( _, chatTabInfo) = await chatTabPool. createTab ( for: kind) {
44+ await send ( . appendAndSelectTab( chatTabInfo) )
4445 }
4546 }
46- guard builder. buildable else { return . none }
47- let chatTap = builder. build ( )
47+
48+ case let . closeTabButtonClicked( id) :
49+ return . run { _ in
50+ chatTabPool. removeTab ( of: id)
51+ }
52+
53+ case let . chatTab( _, . openNewTab( builder) ) :
4854 return . run { send in
49- await send ( . appendAndSelectTab( chatTap) )
55+ if let ( _, chatTabInfo) = await chatTabPool
56+ . createTab ( from: builder. chatTabBuilder)
57+ {
58+ await send ( . appendAndSelectTab( chatTabInfo) )
59+ }
5060 }
5161
5262 default :
@@ -65,12 +75,16 @@ struct GUI: ReducerProtocol {
6575 }
6676
6777 case . createChatGPTChatTabIfNeeded:
68- if state. chatTabGroup. tabs. contains ( where: { $0 is ChatGPTChatTab } ) {
78+ if state. chatTabGroup. tabInfo. contains ( where: {
79+ chatTabPool. getTab ( of: $0. id) is ChatGPTChatTab
80+ } ) {
6981 return . none
7082 }
71- let chatTab = ChatGPTChatTab ( )
72- state. chatTabGroup. tabs. append ( chatTab)
73- return . none
83+ return . run { send in
84+ if let ( _, chatTabInfo) = await chatTabPool. createTab ( for: nil ) {
85+ await send ( . suggestionWidget( . chatPanel( . appendAndSelectTab( chatTabInfo) ) ) )
86+ }
87+ }
7488
7589 case let . sendCustomCommandToActiveChat( command) :
7690 @Sendable func stopAndHandleCommand( _ tab: ChatGPTChatTab ) async {
@@ -80,28 +94,36 @@ struct GUI: ReducerProtocol {
8094 try ? await tab. service. handleCustomCommand ( command)
8195 }
8296
83- if let activeTab = state. chatTabGroup. activeChatTab as? ChatGPTChatTab {
97+ if let info = state. chatTabGroup. selectedTabInfo,
98+ let activeTab = chatTabPool. getTab ( of: info. id) as? ChatGPTChatTab
99+ {
84100 return . run { send in
85101 await send ( . openChatPanel( forceDetach: false ) )
86102 await stopAndHandleCommand ( activeTab)
87103 }
88104 }
89105
90- if let chatTab = state. chatTabGroup. tabs. first ( where: {
91- guard $0 is ChatGPTChatTab else { return false }
92- return true
93- } ) as? ChatGPTChatTab {
106+ if let info = state. chatTabGroup. tabInfo. first ( where: {
107+ chatTabPool. getTab ( of: $0. id) is ChatGPTChatTab
108+ } ) ,
109+ let chatTab = chatTabPool. getTab ( of: info. id) as? ChatGPTChatTab
110+ {
94111 state. chatTabGroup. selectedTabId = chatTab. id
95112 return . run { send in
96113 await send ( . openChatPanel( forceDetach: false ) )
97114 await stopAndHandleCommand ( chatTab)
98115 }
99116 }
100- let chatTab = ChatGPTChatTab ( )
101- state. chatTabGroup. tabs. append ( chatTab)
117+
102118 return . run { send in
119+ guard let ( chatTab, chatTabInfo) = await chatTabPool. createTab ( for: nil ) else {
120+ return
121+ }
122+ await send ( . suggestionWidget( . chatPanel( . appendAndSelectTab( chatTabInfo) ) ) )
103123 await send ( . openChatPanel( forceDetach: false ) )
104- await stopAndHandleCommand ( chatTab)
124+ if let chatTab = chatTab as? ChatGPTChatTab {
125+ await stopAndHandleCommand ( chatTab)
126+ }
105127 }
106128
107129 case . suggestionWidget:
@@ -118,31 +140,28 @@ public final class GraphicalUserInterfaceController {
118140 let widgetController : SuggestionWidgetController
119141 let widgetDataSource : WidgetDataSource
120142 let viewStore : ViewStoreOf < GUI >
143+ let chatTabPool : ChatTabPool
121144
122145 class WeakStoreHolder {
123146 weak var store : StoreOf < GUI > ?
124147 }
125148
126149 private init ( ) {
127- let weakStoreHolder = WeakStoreHolder ( )
150+ let chatTabPool = ChatTabPool ( )
128151 let suggestionDependency = SuggestionWidgetControllerDependency ( )
129152 let setupDependency : ( inout DependencyValues ) -> Void = { dependencies in
130153 dependencies. suggestionWidgetControllerDependency = suggestionDependency
131154 dependencies. suggestionWidgetUserDefaultsObservers = . init( )
132- dependencies. chatTabBuilderCollection = {
133- ChatTabFactory . chatTabBuilderCollection { tab in
134- weakStoreHolder. store?
135- . send ( . suggestionWidget( . chatPanel( . appendAndSelectTab( tab) ) ) )
136- }
137- }
155+ dependencies. chatTabPool = chatTabPool
156+ dependencies. chatTabBuilderCollection = ChatTabFactory . chatTabBuilderCollection
138157 }
139158 let store = StoreOf < GUI > (
140159 initialState: . init( ) ,
141160 reducer: GUI ( ) ,
142161 prepareDependencies: setupDependency
143162 )
144- weakStoreHolder. store = store
145163 self . store = store
164+ self . chatTabPool = chatTabPool
146165 viewStore = ViewStore ( store)
147166 widgetDataSource = . init( )
148167
@@ -151,9 +170,22 @@ public final class GraphicalUserInterfaceController {
151170 state: \. suggestionWidgetState,
152171 action: GUI . Action. suggestionWidget
153172 ) ,
173+ chatTabPool: chatTabPool,
154174 dependency: suggestionDependency
155175 )
156176
177+ chatTabPool. createStore = { id in
178+ store. scope (
179+ state: { state in
180+ state. chatTabGroup. tabInfo [ id: id]
181+ ?? . init( id: id, title: " " )
182+ } ,
183+ action: { childAction in
184+ . suggestionWidget( . chatPanel( . chatTab( id: id, action: childAction) ) )
185+ }
186+ )
187+ }
188+
157189 suggestionDependency. suggestionWidgetDataSource = widgetDataSource
158190 suggestionDependency. onOpenChatClicked = { [ weak self] in
159191 Task { [ weak self] in
@@ -177,3 +209,34 @@ public final class GraphicalUserInterfaceController {
177209 }
178210}
179211
212+ extension ChatTabPool {
213+ @MainActor
214+ func createTab(
215+ from builder: ChatTabBuilder
216+ ) -> ( any ChatTab , ChatTabInfo ) ? {
217+ let id = UUID ( ) . uuidString
218+ let info = ChatTabInfo ( id: id, title: " " )
219+ guard builder. buildable else { return nil }
220+ let chatTap = builder. build ( store: createStore ( id) )
221+ setTab ( chatTap)
222+ return ( chatTap, info)
223+ }
224+
225+ @MainActor
226+ func createTab(
227+ for kind: ChatTabKind ?
228+ ) -> ( any ChatTab , ChatTabInfo ) ? {
229+ let id = UUID ( ) . uuidString
230+ let info = ChatTabInfo ( id: id, title: " " )
231+ guard let builder = kind? . builder else {
232+ let chatTap = ChatGPTChatTab ( store: createStore ( id) )
233+ setTab ( chatTap)
234+ return ( chatTap, info)
235+ }
236+ guard builder. buildable else { return nil }
237+ let chatTap = builder. build ( store: createStore ( id) )
238+ setTab ( chatTap)
239+ return ( chatTap, info)
240+ }
241+ }
242+
0 commit comments