Skip to content

Commit 252efb2

Browse files
committed
WIP
1 parent 8f8909d commit 252efb2

3 files changed

Lines changed: 98 additions & 54 deletions

File tree

Core/Sources/ChatTab/ChatTab.swift

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,50 @@
11
import Foundation
22
import SwiftUI
33

4-
public protocol ChatTab {
5-
associatedtype Body: View
6-
var id: UUID { get }
7-
@ViewBuilder @MainActor var body: Body { get }
4+
open class BaseChatTab: Equatable {
5+
public let id: UUID
6+
7+
public static func == (lhs: BaseChatTab, rhs: BaseChatTab) -> Bool {
8+
lhs.id == rhs.id
9+
}
10+
11+
init(id: UUID) {
12+
self.id = id
13+
}
14+
15+
@ViewBuilder
16+
public var body: some View {
17+
if let tab = self as? ChatTabType {
18+
AnyView(tab.buildView()).id(id)
19+
} else {
20+
EmptyView()
21+
}
22+
}
23+
}
24+
25+
public protocol ChatTabType {
26+
@ViewBuilder
27+
func buildView() -> any View
828
}
929

30+
public typealias ChatTab = BaseChatTab & ChatTabType
31+
1032
public class ChatGPTChatTab: ChatTab {
1133
public var provider: ChatProvider
12-
public var id: UUID { provider.id }
13-
public var body: some View {
34+
35+
public func buildView() -> any View {
1436
ChatPanel(chat: provider)
1537
}
16-
38+
1739
public init(provider: ChatProvider) {
1840
self.provider = provider
41+
super.init(id: provider.id)
1942
}
2043
}
2144

2245
public class EmptyChatTab: ChatTab {
23-
public var id: UUID { .init() }
24-
25-
public var body: some View {
46+
public func buildView() -> any View {
2647
EmptyView()
2748
}
2849
}
50+

Core/Sources/SuggestionWidget/FeatureReducers/ChatPanelFeature.swift

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ import ChatTab
44
import ComposableArchitecture
55
import SwiftUI
66

7-
extension ChatProvider: Equatable {
8-
public static func == (lhs: ChatProvider, rhs: ChatProvider) -> Bool {
9-
lhs.id == rhs.id
10-
}
11-
}
12-
137
struct ChatPanelFeature: ReducerProtocol {
148
struct State: Equatable {
15-
var chat: ChatProvider?
9+
var chatTabs: [BaseChatTab] = []
1610
var colorScheme: ColorScheme = .light
1711
var isPanelDisplayed = false
1812
var chatPanelInASeparateWindow = false
13+
var tabIndex = 0
14+
15+
var currentTab: BaseChatTab? {
16+
if tabIndex >= 0, tabIndex < chatTabs.endIndex {
17+
return chatTabs[tabIndex]
18+
} else {
19+
return nil
20+
}
21+
}
1922
}
2023

2124
enum Action: Equatable {
@@ -24,15 +27,18 @@ struct ChatPanelFeature: ReducerProtocol {
2427
case detachChatPanel
2528
case attachChatPanel
2629
case presentChatPanel(forceDetach: Bool)
27-
case closeChatPanel
2830

2931
case updateContent
30-
case updateChatProvider(ChatProvider?, forceDisplayIfPossible: Bool)
32+
case updateChatTabs([BaseChatTab])
33+
case closeTabButtonClicked(index: Int)
34+
case createNewTapButtonClicked
35+
case tabClicked(index: Int)
3136
}
3237

3338
@Dependency(\.suggestionWidgetControllerDependency) var suggestionWidgetControllerDependency
3439
@Dependency(\.xcodeInspector) var xcodeInspector
35-
@Dependency(\.activeApplicationMonitor) var activeApplicationMonitor
40+
@Dependency(\.activatePreviouslyActiveXcode) var activatePreviouslyActiveXcode
41+
@Dependency(\.activateExtensionService) var activateExtensionService
3642

3743
var body: some ReducerProtocol<State, Action> {
3844
Reduce { state, action in
@@ -41,10 +47,7 @@ struct ChatPanelFeature: ReducerProtocol {
4147
state.isPanelDisplayed = false
4248

4349
return .run { _ in
44-
if let app = activeApplicationMonitor.previousActiveApplication, app.isXcode {
45-
try await Task.sleep(nanoseconds: 200_000_000)
46-
app.activate()
47-
}
50+
await activatePreviouslyActiveXcode()
4851
}
4952

5053
case .toggleChatPanelDetachedButtonClicked:
@@ -59,52 +62,44 @@ struct ChatPanelFeature: ReducerProtocol {
5962
state.chatPanelInASeparateWindow = false
6063
return .none
6164

62-
case .closeChatPanel:
63-
state.chat = nil
64-
return .none
65-
6665
case let .presentChatPanel(forceDetach):
6766
if forceDetach {
6867
state.chatPanelInASeparateWindow = true
6968
}
70-
7169
return .run { send in
72-
guard let provider = await fetchChatProvider(
73-
fileURL: xcodeInspector.activeDocumentURL
74-
) else { return }
75-
await send(.updateChatProvider(provider, forceDisplayIfPossible: true))
76-
77-
try await Task.sleep(nanoseconds: 150_000_000)
78-
await NSApplication.shared.activate(ignoringOtherApps: true)
70+
let tabs = await fetchChatTabs()
71+
await send(.updateChatTabs(tabs))
72+
await activateExtensionService()
7973
}
8074

8175
case .updateContent:
8276
return .run { send in
83-
if let provider = await fetchChatProvider(
84-
fileURL: xcodeInspector.activeDocumentURL
85-
) {
86-
await send(.updateChatProvider(provider, forceDisplayIfPossible: false))
87-
} else {
88-
await send(.updateChatProvider(nil, forceDisplayIfPossible: true))
89-
}
77+
let tabs = await fetchChatTabs()
78+
await send(.updateChatTabs(tabs))
9079
}
9180

92-
case let .updateChatProvider(provider, updateDisplay):
93-
if state.chat?.id != provider?.id {
94-
state.chat = provider
95-
}
96-
if updateDisplay {
97-
state.isPanelDisplayed = provider != nil
98-
}
81+
case let .updateChatTabs(tabs):
82+
state.chatTabs = tabs
83+
state.isPanelDisplayed = !tabs.isEmpty
84+
return .none
85+
86+
case let .closeTabButtonClicked(index):
87+
return .none
88+
89+
case .createNewTapButtonClicked:
90+
return .none
91+
92+
case let .tabClicked(index):
9993
return .none
10094
}
10195
}
10296
}
10397

104-
func fetchChatProvider(fileURL: URL) async -> ChatProvider? {
105-
await suggestionWidgetControllerDependency
106-
.suggestionWidgetDataSource?
107-
.chatForFile(at: fileURL)
98+
func fetchChatTabs() async -> [ChatTab] {
99+
return []
100+
// await suggestionWidgetControllerDependency
101+
// .suggestionWidgetDataSource?
102+
// .chatForFile(at: fileURL)
108103
}
109104
}
110105

Core/Sources/SuggestionWidget/ModuleDependency.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,23 @@ struct ActiveApplicationMonitorKey: DependencyKey {
7171
static let liveValue = ActiveApplicationMonitor.self
7272
}
7373

74+
struct ActivatePreviouslyActiveXcodeKey: DependencyKey {
75+
static let liveValue = { @MainActor in
76+
@Dependency(\.activeApplicationMonitor) var activeApplicationMonitor
77+
if let app = activeApplicationMonitor.previousActiveApplication, app.isXcode {
78+
try? await Task.sleep(nanoseconds: 200_000_000)
79+
app.activate()
80+
}
81+
}
82+
}
83+
84+
struct ActivateExtensionServiceKey: DependencyKey {
85+
static let liveValue = { @MainActor in
86+
try? await Task.sleep(nanoseconds: 150_000_000)
87+
await NSApplication.shared.activate(ignoringOtherApps: true)
88+
}
89+
}
90+
7491
extension DependencyValues {
7592
var suggestionWidgetControllerDependency: SuggestionWidgetControllerDependency {
7693
get { self[SuggestionWidgetControllerDependencyKey.self] }
@@ -96,5 +113,15 @@ extension DependencyValues {
96113
get { self[ActiveApplicationMonitorKey.self] }
97114
set { self[ActiveApplicationMonitorKey.self] = newValue }
98115
}
116+
117+
var activatePreviouslyActiveXcode: () async -> Void {
118+
get { self[ActivatePreviouslyActiveXcodeKey.self] }
119+
set { self[ActivatePreviouslyActiveXcodeKey.self] = newValue }
120+
}
121+
122+
var activateExtensionService: () async -> Void {
123+
get { self[ActivateExtensionServiceKey.self] }
124+
set { self[ActivateExtensionServiceKey.self] = newValue }
125+
}
99126
}
100127

0 commit comments

Comments
 (0)