Skip to content

Commit 43cfbe4

Browse files
committed
Simplify UI injection
1 parent a975e19 commit 43cfbe4

6 files changed

Lines changed: 111 additions & 121 deletions

File tree

Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsCheatsheetSectionView.swift

Lines changed: 0 additions & 71 deletions
This file was deleted.

Core/Sources/HostApp/FeatureSettings/Suggestion/SuggestionSettingsView.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import SharedUIComponents
44
import SwiftUI
55
import XPCShared
66

7-
#if canImport(ProHostApp)
8-
import ProHostApp
9-
#endif
10-
117
struct SuggestionSettingsView: View {
12-
enum Tab {
8+
var tabContainer: ExternalTabContainer {
9+
ExternalTabContainer.tabContainer(for: "SuggestionSettings")
10+
}
11+
12+
enum Tab: Hashable {
1313
case general
14-
case suggestionCheatsheet
14+
case other(String)
1515
}
1616

1717
@State var tabSelection: Tab = .general
@@ -20,7 +20,9 @@ struct SuggestionSettingsView: View {
2020
VStack(spacing: 0) {
2121
Picker("", selection: $tabSelection) {
2222
Text("General").tag(Tab.general)
23-
Text("Cheatsheet").tag(Tab.suggestionCheatsheet)
23+
ForEach(tabContainer.tabs, id: \.id) { tab in
24+
Text(tab.title).tag(Tab.other(tab.id))
25+
}
2426
}
2527
.pickerStyle(.segmented)
2628
.padding(8)
@@ -33,8 +35,8 @@ struct SuggestionSettingsView: View {
3335
switch tabSelection {
3436
case .general:
3537
SuggestionSettingsGeneralSectionView()
36-
case .suggestionCheatsheet:
37-
SuggestionSettingsCheatsheetSectionView()
38+
case let .other(id):
39+
tabContainer.tabView(for: id)
3840
}
3941
}.padding()
4042
}

Core/Sources/HostApp/HostApp.swift

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Foundation
44
import KeyboardShortcuts
55

66
#if canImport(LicenseManagement)
7-
import LicenseManagement
7+
import ProHostApp
88
#endif
99

1010
extension KeyboardShortcuts.Name {
@@ -22,7 +22,6 @@ struct HostApp {
2222

2323
enum Action {
2424
case appear
25-
case informExtensionServiceAboutLicenseKeyChange
2625
case general(General.Action)
2726
case chatModelManagement(ChatModelManagement.Action)
2827
case embeddingModelManagement(EmbeddingModelManagement.Action)
@@ -50,22 +49,10 @@ struct HostApp {
5049
Reduce { _, action in
5150
switch action {
5251
case .appear:
53-
return .none
54-
55-
case .informExtensionServiceAboutLicenseKeyChange:
56-
#if canImport(LicenseManagement)
57-
return .run { _ in
58-
let service = try getService()
59-
do {
60-
try await service
61-
.postNotification(name: Notification.Name.licenseKeyChanged.rawValue)
62-
} catch {
63-
toast(error.localizedDescription, .error)
64-
}
65-
}
66-
#else
67-
return .none
52+
#if canImport(ProHostApp)
53+
ProHostApp.start()
6854
#endif
55+
return .none
6956

7057
case .general:
7158
return .none

Core/Sources/HostApp/TabContainer.swift

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ import ComposableArchitecture
22
import Dependencies
33
import Foundation
44
import LaunchAgentManager
5+
import SharedUIComponents
56
import SwiftUI
67
import Toast
78
import UpdateChecker
89

9-
#if canImport(ProHostApp)
10-
import ProHostApp
11-
#endif
12-
1310
@MainActor
1411
let hostAppStore: StoreOf<HostApp> = .init(initialState: .init(), reducer: { HostApp() })
1512

@@ -19,6 +16,10 @@ public struct TabContainer: View {
1916
@State private var tabBarItems = [TabBarItem]()
2017
@State var tag: Int = 0
2118

19+
var externalTabContainer: ExternalTabContainer {
20+
ExternalTabContainer.tabContainer(for: "TabContainer")
21+
}
22+
2223
public init() {
2324
toastController = ToastControllerDependencyKey.liveValue
2425
store = hostAppStore
@@ -59,15 +60,16 @@ public struct TabContainer: View {
5960
title: "Custom Command",
6061
image: "command.square"
6162
)
62-
#if canImport(ProHostApp)
63-
PlusView(onLicenseKeyChanged: {
64-
store.send(.informExtensionServiceAboutLicenseKeyChange)
65-
}).tabBarItem(
66-
tag: 5,
67-
title: "Plus",
68-
image: "plus.diamond"
69-
)
70-
#endif
63+
64+
ForEach(0..<externalTabContainer.tabs.endIndex, id: \.self) { index in
65+
let tab = externalTabContainer.tabs[index]
66+
tab.viewBuilder().tabBarItem(
67+
tag: 5 + index,
68+
title: tab.title,
69+
image: "plus.diamond"
70+
)
71+
}
72+
7173
DebugSettingsView().tabBarItem(
7274
tag: 4,
7375
title: "Advanced",

Core/Sources/HostApp/SharedComponents/SubSection.swift renamed to Tool/Sources/SharedUIComponents/SubSection.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import SwiftUI
22

3-
struct SubSection<Title: View, Description: View, Content: View>: View {
4-
let title: Title
5-
let description: Description
6-
@ViewBuilder let content: () -> Content
3+
public struct SubSection<Title: View, Description: View, Content: View>: View {
4+
public let title: Title
5+
public let description: Description
6+
@ViewBuilder public let content: () -> Content
77

8-
init(title: Title, description: Description, @ViewBuilder content: @escaping () -> Content) {
8+
public init(title: Title, description: Description, @ViewBuilder content: @escaping () -> Content) {
99
self.title = title
1010
self.description = description
1111
self.content = content
1212
}
1313

14-
var body: some View {
14+
public var body: some View {
1515
VStack(alignment: .leading) {
1616
if !(title is EmptyView && description is EmptyView) {
1717
VStack(alignment: .leading, spacing: 8) {
@@ -43,31 +43,31 @@ struct SubSection<Title: View, Description: View, Content: View>: View {
4343
}
4444
}
4545

46-
extension SubSection where Description == Text {
46+
public extension SubSection where Description == Text {
4747
init(title: Title, description: String, @ViewBuilder content: @escaping () -> Content) {
4848
self.init(title: title, description: Text(description), content: content)
4949
}
5050
}
5151

52-
extension SubSection where Description == EmptyView {
52+
public extension SubSection where Description == EmptyView {
5353
init(title: Title, @ViewBuilder content: @escaping () -> Content) {
5454
self.init(title: title, description: EmptyView(), content: content)
5555
}
5656
}
5757

58-
extension SubSection where Title == EmptyView {
58+
public extension SubSection where Title == EmptyView {
5959
init(description: Description, @ViewBuilder content: @escaping () -> Content) {
6060
self.init(title: EmptyView(), description: description, content: content)
6161
}
6262
}
6363

64-
extension SubSection where Title == EmptyView, Description == EmptyView {
64+
public extension SubSection where Title == EmptyView, Description == EmptyView {
6565
init(@ViewBuilder content: @escaping () -> Content) {
6666
self.init(title: EmptyView(), description: EmptyView(), content: content)
6767
}
6868
}
6969

70-
extension SubSection where Title == EmptyView, Description == Text {
70+
public extension SubSection where Title == EmptyView, Description == Text {
7171
init(description: String, @ViewBuilder content: @escaping () -> Content) {
7272
self.init(title: EmptyView(), description: description, content: content)
7373
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import Dependencies
2+
import Foundation
3+
import SwiftUI
4+
5+
public final class ExternalTabContainer {
6+
public static var tabContainers = [String: ExternalTabContainer]()
7+
8+
public struct TabItem: Identifiable {
9+
public var id: String
10+
public var title: String
11+
public var image: String
12+
public let viewBuilder: () -> AnyView
13+
14+
public init<V: View>(
15+
id: String,
16+
title: String,
17+
image: String = "",
18+
@ViewBuilder viewBuilder: @escaping () -> V
19+
) {
20+
self.id = id
21+
self.title = title
22+
self.image = image
23+
self.viewBuilder = { AnyView(viewBuilder()) }
24+
}
25+
}
26+
27+
public var tabs: [TabItem] = []
28+
public init() { tabs = [] }
29+
30+
public static func tabContainer(for id: String) -> ExternalTabContainer {
31+
if let tabContainer = tabContainers[id] {
32+
return tabContainer
33+
}
34+
let tabContainer = ExternalTabContainer()
35+
tabContainers[id] = tabContainer
36+
return tabContainer
37+
}
38+
39+
@ViewBuilder
40+
public func tabView(for id: String) -> some View {
41+
if let tab = tabs.first(where: { $0.id == id }) {
42+
tab.viewBuilder()
43+
}
44+
}
45+
46+
public func registerTab<V: View>(
47+
id: String,
48+
title: String,
49+
image: String = "",
50+
@ViewBuilder viewBuilder: @escaping () -> V
51+
) {
52+
tabs.append(TabItem(id: id, title: title, image: image, viewBuilder: viewBuilder))
53+
}
54+
55+
public static func registerTab<V: View>(
56+
for tabContainerId: String,
57+
id: String,
58+
title: String,
59+
image: String = "",
60+
@ViewBuilder viewBuilder: @escaping () -> V
61+
) {
62+
tabContainer(for: tabContainerId).registerTab(
63+
id: id,
64+
title: title,
65+
image: image,
66+
viewBuilder: viewBuilder
67+
)
68+
}
69+
}
70+

0 commit comments

Comments
 (0)