@@ -11,23 +11,9 @@ struct ChatWindowView: View {
1111 let store : StoreOf < ChatPanelFeature >
1212 let toggleVisibility : ( Bool ) -> Void
1313
14- struct OverallState : Equatable {
15- var isPanelDisplayed : Bool
16- var colorScheme : ColorScheme
17- var selectedTabId : String ?
18- }
19-
2014 var body : some View {
21- WithViewStore (
22- store,
23- observe: {
24- OverallState (
25- isPanelDisplayed: $0. isPanelDisplayed,
26- colorScheme: $0. colorScheme,
27- selectedTabId: $0. chatTabGroup. selectedTabId
28- )
29- }
30- ) { viewStore in
15+ WithPerceptionTracking {
16+ let _ = store. chatTabGroup. selectedTabId // force re-evaluation
3117 VStack ( spacing: 0 ) {
3218 Rectangle ( ) . fill ( . regularMaterial) . frame ( height: 28 )
3319
@@ -43,10 +29,10 @@ struct ChatWindowView: View {
4329 }
4430 . xcodeStyleFrame ( cornerRadius: 10 )
4531 . ignoresSafeArea ( edges: . top)
46- . onChange ( of: viewStore . state . isPanelDisplayed) { isDisplayed in
32+ . onChange ( of: store . isPanelDisplayed) { isDisplayed in
4733 toggleVisibility ( isDisplayed)
4834 }
49- . preferredColorScheme ( viewStore . state . colorScheme)
35+ . preferredColorScheme ( store . colorScheme)
5036 }
5137 }
5238}
@@ -56,33 +42,33 @@ struct ChatTitleBar: View {
5642 @State var isHovering = false
5743
5844 var body : some View {
59- HStack ( spacing: 6 ) {
60- Button ( action: {
61- store. send ( . closeActiveTabClicked)
62- } ) {
63- EmptyView ( )
64- }
65- . opacity ( 0 )
66- . keyboardShortcut ( " w " , modifiers: [ . command] )
45+ WithPerceptionTracking {
46+ HStack ( spacing: 6 ) {
47+ Button ( action: {
48+ store. send ( . closeActiveTabClicked)
49+ } ) {
50+ EmptyView ( )
51+ }
52+ . opacity ( 0 )
53+ . keyboardShortcut ( " w " , modifiers: [ . command] )
6754
68- Button (
69- action: {
70- store. send ( . hideButtonClicked)
55+ Button (
56+ action: {
57+ store. send ( . hideButtonClicked)
58+ }
59+ ) {
60+ Image ( systemName: " minus " )
61+ . foregroundStyle ( . black. opacity ( 0.5 ) )
62+ . font ( Font . system ( size: 8 ) . weight ( . heavy) )
7163 }
72- ) {
73- Image ( systemName: " minus " )
74- . foregroundStyle ( . black. opacity ( 0.5 ) )
75- . font ( Font . system ( size: 8 ) . weight ( . heavy) )
76- }
77- . opacity ( 0 )
78- . keyboardShortcut ( " m " , modifiers: [ . command] )
64+ . opacity ( 0 )
65+ . keyboardShortcut ( " m " , modifiers: [ . command] )
7966
80- Spacer ( )
67+ Spacer ( )
8168
82- WithViewStore ( store, observe: { $0. isDetached } ) { viewStore in
8369 TrafficLightButton (
8470 isHovering: isHovering,
85- isActive: viewStore . state ,
71+ isActive: store . isDetached ,
8672 color: Color ( nsColor: . systemCyan) ,
8773 action: {
8874 store. send ( . toggleChatPanelDetachedButtonClicked)
@@ -94,12 +80,12 @@ struct ChatTitleBar: View {
9480 . transformEffect ( . init( translationX: 0 , y: 0.5 ) )
9581 }
9682 }
83+ . buttonStyle ( . plain)
84+ . padding ( . trailing, 8 )
85+ . onHover ( perform: { hovering in
86+ isHovering = hovering
87+ } )
9788 }
98- . buttonStyle ( . plain)
99- . padding ( . trailing, 8 )
100- . onHover ( perform: { hovering in
101- isHovering = hovering
102- } )
10389 }
10490
10591 struct TrafficLightButton < Icon: View > : View {
@@ -157,30 +143,44 @@ struct ChatTabBar: View {
157143 var selectedTabId : String
158144 }
159145
160- @Environment ( \. chatTabPool) var chatTabPool
161- @State var draggingTabId : String ?
162-
163146 var body : some View {
164- WithViewStore (
165- store,
166- observe: { TabBarState (
167- tabInfo: $0. chatTabGroup. tabInfo,
168- selectedTabId: $0. chatTabGroup. selectedTabId
169- ?? $0. chatTabGroup. tabInfo. first? . id ?? " "
170- ) }
171- ) { viewStore in
172- HStack ( spacing: 0 ) {
147+ HStack ( spacing: 0 ) {
148+ Divider ( )
149+ Tabs ( store: store)
150+ CreateButton ( store: store)
151+ }
152+ . background {
153+ Button ( action: { store. send ( . switchToNextTab) } ) { EmptyView ( ) }
154+ . opacity ( 0 )
155+ . keyboardShortcut ( " ] " , modifiers: [ . command, . shift] )
156+ Button ( action: { store. send ( . switchToPreviousTab) } ) { EmptyView ( ) }
157+ . opacity ( 0 )
158+ . keyboardShortcut ( " [ " , modifiers: [ . command, . shift] )
159+ }
160+ }
161+
162+ struct Tabs : View {
163+ let store : StoreOf < ChatPanelFeature >
164+ @State var draggingTabId : String ?
165+ @Environment ( \. chatTabPool) var chatTabPool
166+
167+ var body : some View {
168+ WithPerceptionTracking {
169+ let tabInfo = store. chatTabGroup. tabInfo
170+ let selectedTabId = store. chatTabGroup. selectedTabId
171+ ?? store. chatTabGroup. tabInfo. first? . id
172+ ?? " "
173173 ScrollViewReader { proxy in
174174 ScrollView ( . horizontal) {
175175 HStack ( spacing: 0 ) {
176- ForEach ( viewStore . state . tabInfo, id: \. id) { info in
176+ ForEach ( tabInfo, id: \. id) { info in
177177 if let tab = chatTabPool. getTab ( of: info. id) {
178178 ChatTabBarButton (
179179 store: store,
180180 info: info,
181181 content: { tab. tabItem } ,
182182 icon: { tab. icon } ,
183- isSelected: info. id == viewStore . state . selectedTabId
183+ isSelected: info. id == selectedTabId
184184 )
185185 . contextMenu {
186186 tab. menu
@@ -194,7 +194,7 @@ struct ChatTabBar: View {
194194 of: [ . text] ,
195195 delegate: ChatTabBarDropDelegate (
196196 store: store,
197- tabs: viewStore . state . tabInfo,
197+ tabs: tabInfo,
198198 itemId: info. id,
199199 draggingTabId: $draggingTabId
200200 )
@@ -207,72 +207,61 @@ struct ChatTabBar: View {
207207 }
208208 }
209209 . hideScrollIndicator ( )
210- . onChange ( of: viewStore . selectedTabId) { id in
210+ . onChange ( of: selectedTabId) { id in
211211 withAnimation ( . easeInOut( duration: 0.2 ) ) {
212212 proxy. scrollTo ( id)
213213 }
214214 }
215215 }
216-
217- Divider ( )
218-
219- createButton
220216 }
221217 }
222- . background {
223- Button ( action: { store. send ( . switchToNextTab) } ) { EmptyView ( ) }
224- . opacity ( 0 )
225- . keyboardShortcut ( " ] " , modifiers: [ . command, . shift] )
226- Button ( action: { store. send ( . switchToPreviousTab) } ) { EmptyView ( ) }
227- . opacity ( 0 )
228- . keyboardShortcut ( " [ " , modifiers: [ . command, . shift] )
229- }
230218 }
231219
232- @ ViewBuilder
233- var createButton : some View {
234- Menu {
235- WithViewStore ( store , observe : { $0 . chatTabGroup . tabCollection } ) { viewStore in
236- ForEach ( 0 ..< viewStore . state . endIndex , id : \ . self ) { index in
237- switch viewStore . state [ index ] {
238- case let . kind ( kind ) :
239- Button ( action : {
240- store . send ( . createNewTapButtonClicked ( kind : kind ) )
241- } ) {
242- Text ( kind . title )
243- } . disabled ( kind . builder is DisabledChatTabBuilder )
244- case let . folder ( title , list ) :
245- Menu {
246- ForEach ( 0 ..< list . endIndex , id : \ . self ) { index in
247- Button ( action : {
248- store
249- . send (
250- . createNewTapButtonClicked (
251- kind : list [ index ]
252- )
220+ struct CreateButton : View {
221+ let store : StoreOf < ChatPanelFeature >
222+
223+ var body : some View {
224+ WithPerceptionTracking {
225+ let collection = store . chatTabGroup . tabCollection
226+ Menu {
227+ ForEach ( 0 ..< collection . endIndex , id : \ . self ) { index in
228+ switch collection [ index ] {
229+ case let . kind ( kind ) :
230+ Button ( action : {
231+ store . send ( . createNewTapButtonClicked ( kind : kind ) )
232+ } ) {
233+ Text ( kind . title )
234+ } . disabled ( kind . builder is DisabledChatTabBuilder )
235+ case let . folder ( title , list ) :
236+ Menu {
237+ ForEach ( 0 ..< list . endIndex , id : \ . self ) { index in
238+ Button ( action : {
239+ store . send (
240+ . createNewTapButtonClicked ( kind : list [ index ] )
253241 )
254- } ) {
255- Text ( list [ index] . title)
242+ } ) {
243+ Text ( list [ index] . title)
244+ }
256245 }
246+ } label: {
247+ Text ( title)
257248 }
258- } label: {
259- Text ( title)
260249 }
261250 }
251+ } label: {
252+ Image ( systemName: " plus " )
253+ } primaryAction: {
254+ store. send ( . createNewTapButtonClicked( kind: nil ) )
255+ }
256+ . foregroundColor ( . secondary)
257+ . menuStyle ( . borderedButton)
258+ . padding ( . horizontal, 4 )
259+ . fixedSize ( horizontal: true , vertical: false )
260+ . onHover { isHovering in
261+ if isHovering {
262+ store. send ( . createNewTapButtonHovered)
263+ }
262264 }
263- }
264- } label: {
265- Image ( systemName: " plus " )
266- } primaryAction: {
267- store. send ( . createNewTapButtonClicked( kind: nil ) )
268- }
269- . foregroundColor ( . secondary)
270- . menuStyle ( . borderedButton)
271- . padding ( . horizontal, 4 )
272- . fixedSize ( horizontal: true , vertical: false )
273- . onHover { isHovering in
274- if isHovering {
275- store. send ( . createNewTapButtonHovered)
276265 }
277266 }
278267 }
@@ -349,32 +338,22 @@ struct ChatTabBarButton<Content: View, Icon: View>: View {
349338
350339struct ChatTabContainer : View {
351340 let store : StoreOf < ChatPanelFeature >
352-
353- struct TabContainerState : Equatable {
354- var tabInfo : IdentifiedArray < String , ChatTabInfo >
355- var selectedTabId : String ?
356- }
357-
358341 @Environment ( \. chatTabPool) var chatTabPool
359342
360343 var body : some View {
361- WithViewStore (
362- store,
363- observe: {
364- TabContainerState (
365- tabInfo: $0. chatTabGroup. tabInfo,
366- selectedTabId: $0. chatTabGroup. selectedTabId
367- ?? $0. chatTabGroup. tabInfo. first? . id ?? " "
368- )
369- }
370- ) { viewStore in
344+ WithPerceptionTracking {
345+ let tabInfo = store. chatTabGroup. tabInfo
346+ let selectedTabId = store. chatTabGroup. selectedTabId
347+ ?? store. chatTabGroup. tabInfo. first? . id
348+ ?? " "
349+
371350 ZStack {
372- if viewStore . state . tabInfo. isEmpty {
351+ if tabInfo. isEmpty {
373352 Text ( " Empty " )
374353 } else {
375- ForEach ( viewStore . state . tabInfo) { tabInfo in
354+ ForEach ( tabInfo) { tabInfo in
376355 if let tab = chatTabPool. getTab ( of: tabInfo. id) {
377- let isActive = tab. id == viewStore . state . selectedTabId
356+ let isActive = tab. id == selectedTabId
378357 tab. body
379358 . opacity ( isActive ? 1 : 0 )
380359 . disabled ( !isActive)
@@ -428,12 +407,12 @@ struct ChatWindowView_Previews: PreviewProvider {
428407 . init( id: " 5 " , title: " Empty-5 " ) ,
429408 . init( id: " 6 " , title: " Empty-6 " ) ,
430409 . init( id: " 7 " , title: " Empty-7 " ) ,
431- ] ,
410+ ] as IdentifiedArray < String , ChatTabInfo > ,
432411 selectedTabId: " 2 "
433412 ) ,
434413 isPanelDisplayed: true
435414 ) ,
436- reducer: ChatPanelFeature ( )
415+ reducer: { ChatPanelFeature ( ) }
437416 )
438417 }
439418
0 commit comments