Skip to content

Commit 057c9d0

Browse files
authored
Add servers as filter to pick entities in the EntityPicker flow (#3444)
1 parent ded3413 commit 057c9d0

File tree

7 files changed

+150
-48
lines changed

7 files changed

+150
-48
lines changed

HomeAssistant.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@
798798
42C1012B2CD3DB8A0012BA78 /* CoverIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C101292CD3DB8A0012BA78 /* CoverIntent.swift */; };
799799
42C1012E2CD3DBF00012BA78 /* ControlCover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C1012C2CD3DBF00012BA78 /* ControlCover.swift */; };
800800
42C101302CD3DC0C0012BA78 /* ControlCoverValueProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C1012F2CD3DC0C0012BA78 /* ControlCoverValueProvider.swift */; };
801+
42C131D02D66084C00AF48E6 /* PillView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C131CF2D66084C00AF48E6 /* PillView.swift */; };
801802
42C3737F2BC415AC00898990 /* UIViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C3737E2BC415AC00898990 /* UIViewController+Extensions.swift */; };
802803
42C373B22BC5382900898990 /* HostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C373B12BC5382900898990 /* HostingController.swift */; };
803804
42CE8FA72B45D1E900C707F9 /* CoreStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42CE8FA52B45D1E900C707F9 /* CoreStrings.swift */; };
@@ -856,6 +857,7 @@
856857
42EFFAEC2C8882DD002F10FC /* CarPlayConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42EFFAEB2C8882DD002F10FC /* CarPlayConfigurationView.swift */; };
857858
42F158462CA15C99009C7201 /* ControlSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F158452CA15C99009C7201 /* ControlSwitch.swift */; };
858859
42F158482CA15FA7009C7201 /* SwitchIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F158472CA15FA7009C7201 /* SwitchIntent.swift */; };
860+
42F161442D661D11003DDC75 /* ServersPickerPillList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F161422D661CBA003DDC75 /* ServersPickerPillList.swift */; };
859861
42F1DA5B2B4BF7DF002729BC /* WindowSizeObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F1DA5A2B4BF7DF002729BC /* WindowSizeObserver.swift */; };
860862
42F1DA5D2B4BF85F002729BC /* WindowScenesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F1DA5C2B4BF85F002729BC /* WindowScenesManager.swift */; };
861863
42F1DA5F2B4D4B32002729BC /* CarPlayServerListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F1DA5E2B4D4B32002729BC /* CarPlayServerListTemplate.swift */; };
@@ -2137,6 +2139,7 @@
21372139
42C101292CD3DB8A0012BA78 /* CoverIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverIntent.swift; sourceTree = "<group>"; };
21382140
42C1012C2CD3DBF00012BA78 /* ControlCover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlCover.swift; sourceTree = "<group>"; };
21392141
42C1012F2CD3DC0C0012BA78 /* ControlCoverValueProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlCoverValueProvider.swift; sourceTree = "<group>"; };
2142+
42C131CF2D66084C00AF48E6 /* PillView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillView.swift; sourceTree = "<group>"; };
21402143
42C3737E2BC415AC00898990 /* UIViewController+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extensions.swift"; sourceTree = "<group>"; };
21412144
42C373AF2BC536AA00898990 /* WatchApp-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WatchApp-Bridging-Header.h"; sourceTree = "<group>"; };
21422145
42C373B12BC5382900898990 /* HostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostingController.swift; sourceTree = "<group>"; };
@@ -2188,6 +2191,7 @@
21882191
42EFFAEB2C8882DD002F10FC /* CarPlayConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarPlayConfigurationView.swift; sourceTree = "<group>"; };
21892192
42F158452CA15C99009C7201 /* ControlSwitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlSwitch.swift; sourceTree = "<group>"; };
21902193
42F158472CA15FA7009C7201 /* SwitchIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchIntent.swift; sourceTree = "<group>"; };
2194+
42F161422D661CBA003DDC75 /* ServersPickerPillList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersPickerPillList.swift; sourceTree = "<group>"; };
21912195
42F1DA5A2B4BF7DF002729BC /* WindowSizeObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowSizeObserver.swift; sourceTree = "<group>"; };
21922196
42F1DA5C2B4BF85F002729BC /* WindowScenesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowScenesManager.swift; sourceTree = "<group>"; };
21932197
42F1DA5E2B4D4B32002729BC /* CarPlayServerListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarPlayServerListTemplate.swift; sourceTree = "<group>"; };
@@ -4298,13 +4302,15 @@
42984302
42CA28B12B101D9C0093B31A /* Components */ = {
42994303
isa = PBXGroup;
43004304
children = (
4305+
42F161422D661CBA003DDC75 /* ServersPickerPillList.swift */,
43014306
429BEA1B2D1030EA00F070F9 /* SheetCloseButton.swift */,
43024307
42FCD0052B9B1D9E0057783F /* CollapsibleView.swift */,
43034308
42CA28AF2B101D6B0093B31A /* CardView.swift */,
43044309
42CA28B52B1022680093B31A /* HAButton.swift */,
43054310
42790C452C4808FA00E31B38 /* AppleLikeBottomSheet.swift */,
43064311
4254C4CC2D103F7B00245021 /* ExternalLinkButton.swift */,
43074312
42B74A5C2D36A47E00C37304 /* CloseButton.swift */,
4313+
42C131CF2D66084C00AF48E6 /* PillView.swift */,
43084314
);
43094315
path = Components;
43104316
sourceTree = "<group>";
@@ -7571,6 +7577,7 @@
75717577
118F046924CB895A00CBBD5C /* UIColor+CSS3+Hex.swift in Sources */,
75727578
1109F81F24A1C011002590F2 /* SensorProvider.swift in Sources */,
75737579
4254C4CA2D103ABB00245021 /* ExternalLink.swift in Sources */,
7580+
42F161442D661D11003DDC75 /* ServersPickerPillList.swift in Sources */,
75747581
4297ADA82C89C74A00790812 /* GRDB+Initialization.swift in Sources */,
75757582
1141182A24AFA10900E6525C /* WebhookResponseHandler.swift in Sources */,
75767583
420CFC792D3F9CAB009A94F3 /* AppEntityRegistryListForDisplayTable.swift in Sources */,
@@ -7761,6 +7768,7 @@
77617768
D05A4D32216DD206009FD1EB /* MJPEGStreamer.swift in Sources */,
77627769
426266452C11B02C0081A818 /* InteractiveImmediateMessages.swift in Sources */,
77637770
42CE8FB02B46C3D900C707F9 /* CoreStrings+Values.swift in Sources */,
7771+
42C131D02D66084C00AF48E6 /* PillView.swift in Sources */,
77647772
11C4628B24B1230E00031902 /* WebhookResponseServiceCall.swift in Sources */,
77657773
D0EEF30A214DD64C00D1D360 /* UIImage+Icons.swift in Sources */,
77667774
42B94BED2B96083C00DEE060 /* AssistModel.swift in Sources */,

Sources/App/Settings/ClientEventsLogView/ClientEventsLogView.swift

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,21 @@ struct ClientEventsLogView: View {
9090
Button {
9191
viewModel.resetTypeFilter()
9292
} label: {
93-
filterPill(L10n.ClientEvents.EventType.all, selected: viewModel.typeFilter == nil)
93+
PillView(
94+
text: L10n.ClientEvents.EventType.all,
95+
selected: viewModel.typeFilter == nil
96+
)
9497
}
9598
ForEach(ClientEvent.EventType.allCases.sorted { e1, e2 in
9699
e1.displayText < e2.displayText
97100
}, id: \.self) { type in
98101
Button {
99102
viewModel.typeFilter = type
100103
} label: {
101-
filterPill(type.displayText, selected: viewModel.typeFilter == type)
104+
PillView(
105+
text: type.displayText,
106+
selected: viewModel.typeFilter == type
107+
)
102108
}
103109
}
104110
}
@@ -124,15 +130,6 @@ struct ClientEventsLogView: View {
124130
}
125131
}
126132

127-
private func filterPill(_ text: String, selected: Bool) -> some View {
128-
Text(text)
129-
.foregroundStyle(selected ? .white : Color(uiColor: .label))
130-
.padding(Spaces.one)
131-
.padding(.horizontal)
132-
.background(selected ? Color.asset(Asset.Colors.haPrimary) : Color.secondary.opacity(0.1))
133-
.clipShape(Capsule())
134-
}
135-
136133
private func listItem(_ event: ClientEvent) -> some View {
137134
NavigationLink {
138135
eventDescription(event)

Sources/App/Settings/MagicItem/Add/MagicItemAddView.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ struct MagicItemAddView: View {
6565
.onAppear {
6666
autoSelectItemType()
6767
viewModel.loadContent()
68+
69+
if viewModel.selectedServerId == nil {
70+
viewModel.selectedServerId = Current.servers.all.first?.identifier.rawValue
71+
}
6872
}
6973
.toolbar(content: {
7074
CloseButton {
@@ -159,10 +163,10 @@ struct MagicItemAddView: View {
159163

160164
@ViewBuilder
161165
private var entitiesPerServerList: some View {
162-
ForEach(Array(viewModel.entities.keys), id: \.identifier) { server in
163-
Section(server.info.name) {
164-
list(entities: viewModel.entities[server] ?? [], serverId: server.identifier.rawValue, type: .entity)
165-
}
166+
ServersPickerPillList(selectedServerId: $viewModel.selectedServerId)
167+
if let server = Current.servers.all
168+
.first(where: { $0.identifier.rawValue == viewModel.selectedServerId }) ?? Current.servers.all.first {
169+
list(entities: viewModel.entities[server] ?? [], serverId: server.identifier.rawValue, type: .entity)
166170
}
167171
}
168172

Sources/App/Settings/MagicItem/Add/MagicItemAddViewModel.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ final class MagicItemAddViewModel: ObservableObject {
1818
@Published var entities: [Server: [HAAppEntity]] = [:]
1919
@Published var actions: [Action] = []
2020
@Published var searchText: String = ""
21+
@Published var selectedServerId: String?
2122

2223
private var entitiesSubscription: AnyCancellable?
2324

Sources/App/Settings/MagicItem/EntityPicker.swift

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,21 @@ struct EntityPicker: View {
99
@State private var entities: [HAAppEntity] = []
1010
@Binding private var selectedEntity: HAAppEntity?
1111
@State private var searchTerm = ""
12+
@State private var selectedServerId: String?
1213

1314
init(selectedEntity: Binding<HAAppEntity?>, domainFilter: Domain?) {
1415
self.domainFilter = domainFilter
1516
self._selectedEntity = selectedEntity
1617
}
1718

1819
var body: some View {
20+
button
21+
.sheet(isPresented: $showList) {
22+
screen
23+
}
24+
}
25+
26+
private var button: some View {
1927
Button(action: {
2028
showList = true
2129
}, label: {
@@ -25,45 +33,54 @@ struct EntityPicker: View {
2533
Text(L10n.EntityPicker.placeholder)
2634
}
2735
})
28-
.sheet(isPresented: $showList) {
29-
NavigationView {
30-
List {
31-
ForEach(Current.servers.all, id: \.identifier) { server in
32-
Section(server.info.name) {
33-
ForEach(entities.filter({ entity in
34-
if searchTerm.count > 2 {
35-
return entity.serverId == server.identifier.rawValue && (
36-
entity.name.lowercased().contains(searchTerm.lowercased()) ||
37-
entity.entityId.lowercased().contains(searchTerm.lowercased())
38-
)
36+
}
37+
38+
private var screen: some View {
39+
NavigationView {
40+
List {
41+
ServersPickerPillList(selectedServerId: $selectedServerId)
42+
ForEach(entities.filter({ entity in
43+
if searchTerm.count > 2 {
44+
return entity.serverId == selectedServerId && (
45+
entity.name.lowercased().contains(searchTerm.lowercased()) ||
46+
entity.entityId.lowercased().contains(searchTerm.lowercased())
47+
)
48+
} else {
49+
return entity.serverId == selectedServerId
50+
}
51+
}), id: \.id) { entity in
52+
Button(action: {
53+
selectedEntity = entity
54+
showList = false
55+
}, label: {
56+
VStack {
57+
Group {
58+
if let selectedEntity, selectedEntity == entity {
59+
Label(entity.name, systemSymbol: .checkmark)
3960
} else {
40-
return entity.serverId == server.identifier.rawValue
61+
Text(entity.name)
4162
}
42-
}), id: \.id) { entity in
43-
Button(action: {
44-
selectedEntity = entity
45-
showList = false
46-
}, label: {
47-
if let selectedEntity, selectedEntity == entity {
48-
Label(entity.name, systemSymbol: .checkmark)
49-
} else {
50-
Text(entity.name)
51-
}
52-
})
53-
.tint(.accentColor)
63+
Text(entity.entityId)
64+
.font(.footnote)
65+
.foregroundStyle(.secondary)
5466
}
67+
.frame(maxWidth: .infinity, alignment: .leading)
5568
}
56-
}
69+
})
70+
.tint(.accentColor)
5771
}
58-
.searchable(text: $searchTerm)
59-
.onAppear {
60-
fetchEntities()
72+
}
73+
.searchable(text: $searchTerm)
74+
.onAppear {
75+
fetchEntities()
76+
if selectedServerId == nil {
77+
selectedServerId = Current.servers.all.first?.identifier.rawValue
6178
}
62-
.toolbar {
63-
ToolbarItem(placement: .topBarTrailing) {
64-
CloseButton {
65-
showList = false
66-
}
79+
}
80+
.toolbar {
81+
ToolbarItem(placement: .topBarTrailing) {
82+
CloseButton {
83+
showList = false
6784
}
6885
}
6986
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import SwiftUI
2+
3+
public struct PillView: View {
4+
private let selected: Bool
5+
private let text: String
6+
7+
public init(text: String, selected: Bool) {
8+
self.text = text
9+
self.selected = selected
10+
}
11+
12+
public var body: some View {
13+
Text(text)
14+
.foregroundStyle(selected ? .white : Color(uiColor: .label))
15+
.padding(Spaces.one)
16+
.padding(.horizontal)
17+
.background(selected ? Color.asset(Asset.Colors.haPrimary) : Color.secondary.opacity(0.1))
18+
.clipShape(Capsule())
19+
}
20+
}
21+
22+
#Preview {
23+
HStack {
24+
PillView(text: "Value1", selected: true)
25+
PillView(text: "Value2", selected: false)
26+
PillView(text: "Value3", selected: false)
27+
}
28+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import SwiftUI
2+
3+
public struct ServersPickerPillList: View {
4+
@Binding private var selectedServerId: String?
5+
6+
public init(selectedServerId: Binding<String?>) {
7+
self._selectedServerId = selectedServerId
8+
}
9+
10+
public var body: some View {
11+
serversList
12+
}
13+
14+
@ViewBuilder
15+
private var serversList: some View {
16+
if Current.servers.all.count > 1 {
17+
Section {
18+
ScrollView(.horizontal) {
19+
HStack {
20+
ForEach(Current.servers.all.sorted(by: { lhs, rhs in
21+
lhs.info.sortOrder < rhs.info.sortOrder
22+
}), id: \.identifier) { server in
23+
Button {
24+
selectedServerId = server.identifier.rawValue
25+
} label: {
26+
PillView(
27+
text: server.info.name,
28+
selected: selectedServerId == server.identifier.rawValue
29+
)
30+
}
31+
}
32+
}
33+
.frame(maxWidth: .infinity, alignment: .leading)
34+
}
35+
.frame(maxWidth: .infinity, alignment: .leading)
36+
}
37+
.listRowBackground(Color.clear)
38+
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
39+
}
40+
}
41+
}
42+
43+
#Preview {
44+
List {
45+
ServersPickerPillList(selectedServerId: .constant("1"))
46+
}
47+
}

0 commit comments

Comments
 (0)