Skip to content

Commit c474fb5

Browse files
Copilotbgoncal
andcommitted
Add background selection feature with 10 mesh gradient options
Co-authored-by: bgoncal <5808343+bgoncal@users.noreply.github.com>
1 parent cf7bba6 commit c474fb5

File tree

5 files changed

+301
-8
lines changed

5 files changed

+301
-8
lines changed

HomeAssistant.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@
577577
420C1BB22CF7DA9100AF22E7 /* ClientEventsLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420C1BB12CF7DA9100AF22E7 /* ClientEventsLogView.swift */; };
578578
420C1BB52CF7DC1400AF22E7 /* ClientEventsLogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420C1BB42CF7DC1400AF22E7 /* ClientEventsLogViewModel.swift */; };
579579
420C91502F0C6CAC005D04A6 /* HomeViewCustomizationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420C914F2F0C6CAC005D04A6 /* HomeViewCustomizationView.swift */; };
580+
D82C94AEE56A428D90BE806B /* HomeViewBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B8A39CB50D4A93BDB1D756 /* HomeViewBackground.swift */; };
580581
420C91522F0C7988005D04A6 /* EntityRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420C91512F0C7988005D04A6 /* EntityRegistry.swift */; };
581582
420C91532F0C7988005D04A6 /* EntityRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420C91512F0C7988005D04A6 /* EntityRegistry.swift */; };
582583
420CFC642D3F9C2C009A94F3 /* HAppEntityTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 420CFC632D3F9C2C009A94F3 /* HAppEntityTable.swift */; };
@@ -2249,6 +2250,7 @@
22492250
420C1BB12CF7DA9100AF22E7 /* ClientEventsLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientEventsLogView.swift; sourceTree = "<group>"; };
22502251
420C1BB42CF7DC1400AF22E7 /* ClientEventsLogViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientEventsLogViewModel.swift; sourceTree = "<group>"; };
22512252
420C914F2F0C6CAC005D04A6 /* HomeViewCustomizationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewCustomizationView.swift; sourceTree = "<group>"; };
2253+
34B8A39CB50D4A93BDB1D756 /* HomeViewBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewBackground.swift; sourceTree = "<group>"; };
22522254
420C91512F0C7988005D04A6 /* EntityRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityRegistry.swift; sourceTree = "<group>"; };
22532255
420C91542F0C7AB4005D04A6 /* EntityRegistry.test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityRegistry.test.swift; sourceTree = "<group>"; };
22542256
420CFC632D3F9C2C009A94F3 /* HAppEntityTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HAppEntityTable.swift; sourceTree = "<group>"; };
@@ -5707,6 +5709,7 @@
57075709
42F5C0822F02B6DB00C50310 /* HomeSectionsReorderView.swift */,
57085710
4289FCB82F0BFF50005189AA /* HomeViewConfiguration.swift */,
57095711
420C914F2F0C6CAC005D04A6 /* HomeViewCustomizationView.swift */,
5712+
34B8A39CB50D4A93BDB1D756 /* HomeViewBackground.swift */,
57105713
);
57115714
path = Home;
57125715
sourceTree = "<group>";
@@ -9151,6 +9154,7 @@
91519154
42B94BDF2B9606CD00DEE060 /* AssistView.swift in Sources */,
91529155
1185DF94271FBA6100ED7D9A /* OnboardingAuthDetails.swift in Sources */,
91539156
420C91502F0C6CAC005D04A6 /* HomeViewCustomizationView.swift in Sources */,
9157+
D82C94AEE56A428D90BE806B /* HomeViewBackground.swift in Sources */,
91549158
4273C48B2C8858470065A5B4 /* ControlOpenPageValueProvider.swift in Sources */,
91559159
427FEE072D9C03DE0047C00C /* OnboardingScanningInstanceRow.swift in Sources */,
91569160
420FE84B2B556BB100878E06 /* CarPlayActionsTemplate+Build.swift in Sources */,

Sources/App/WebView/ExperimentalSpace/Home/HomeView.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ struct HomeView: View {
3131
.toolbar {
3232
toolbarMenu
3333
}
34-
.background(Color.secondaryBackground)
34+
.background {
35+
backgroundView
36+
}
3537
}
3638
.onAppear {
3739
Task {
@@ -386,6 +388,18 @@ struct HomeView: View {
386388
}
387389
}
388390
}
391+
392+
// MARK: - Background
393+
394+
@ViewBuilder
395+
private var backgroundView: some View {
396+
if #available(iOS 18.0, *),
397+
let backgroundId = viewModel.configuration.selectedBackgroundId {
398+
HomeViewBackgroundView(backgroundId: backgroundId)
399+
} else {
400+
Color.secondaryBackground
401+
}
402+
}
389403
}
390404

391405
@available(iOS 26.0, *)
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import Foundation
2+
import SwiftUI
3+
4+
/// Background type for HomeView
5+
enum HomeViewBackgroundType: String, Codable, CaseIterable {
6+
case image
7+
case view
8+
}
9+
10+
/// Background option for HomeView
11+
struct HomeViewBackgroundOption: Codable, Equatable, Identifiable {
12+
let id: String
13+
let name: String
14+
let type: HomeViewBackgroundType
15+
16+
static let allOptions: [HomeViewBackgroundOption] = [
17+
.init(id: "gradient1", name: "Ocean Blue", type: .view),
18+
.init(id: "gradient2", name: "Sky Dreams", type: .view),
19+
.init(id: "gradient3", name: "Deep Sea", type: .view),
20+
.init(id: "gradient4", name: "Azure Wave", type: .view),
21+
.init(id: "gradient5", name: "Midnight Blue", type: .view),
22+
.init(id: "gradient6", name: "Crystal Waters", type: .view),
23+
.init(id: "gradient7", name: "Electric Blue", type: .view),
24+
.init(id: "gradient8", name: "Sapphire Glow", type: .view),
25+
.init(id: "gradient9", name: "Arctic Flow", type: .view),
26+
.init(id: "gradient10", name: "Blue Horizon", type: .view),
27+
]
28+
29+
static let defaultOption = allOptions[0]
30+
}
31+
32+
@available(iOS 18.0, *)
33+
struct HomeViewBackgroundView: View {
34+
let backgroundId: String
35+
36+
var body: some View {
37+
switch backgroundId {
38+
case "gradient1":
39+
oceanBlueGradient
40+
case "gradient2":
41+
skyDreamsGradient
42+
case "gradient3":
43+
deepSeaGradient
44+
case "gradient4":
45+
azureWaveGradient
46+
case "gradient5":
47+
midnightBlueGradient
48+
case "gradient6":
49+
crystalWatersGradient
50+
case "gradient7":
51+
electricBlueGradient
52+
case "gradient8":
53+
sapphireGlowGradient
54+
case "gradient9":
55+
arcticFlowGradient
56+
case "gradient10":
57+
blueHorizonGradient
58+
default:
59+
oceanBlueGradient
60+
}
61+
}
62+
63+
// MARK: - Gradient Backgrounds
64+
65+
private var oceanBlueGradient: some View {
66+
MeshGradient(
67+
width: 3,
68+
height: 3,
69+
points: [
70+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
71+
[0.0, 0.5], [0.5, 0.5], [1.0, 0.5],
72+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
73+
],
74+
colors: [
75+
.blue20, .blue30, .blue40,
76+
.blue30, .blue50, .blue60,
77+
.blue40, .blue60, .blue70,
78+
]
79+
)
80+
.ignoresSafeArea()
81+
}
82+
83+
private var skyDreamsGradient: some View {
84+
MeshGradient(
85+
width: 3,
86+
height: 3,
87+
points: [
88+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
89+
[0.0, 0.5], [0.5, 0.5], [1.0, 0.5],
90+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
91+
],
92+
colors: [
93+
.blue70, .blue80, .blue90,
94+
.blue60, .blue70, .blue80,
95+
.blue50, .blue60, .blue70,
96+
]
97+
)
98+
.ignoresSafeArea()
99+
}
100+
101+
private var deepSeaGradient: some View {
102+
MeshGradient(
103+
width: 3,
104+
height: 3,
105+
points: [
106+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
107+
[0.0, 0.5], [0.5, 0.5], [1.0, 0.5],
108+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
109+
],
110+
colors: [
111+
.blue05, .blue10, .blue20,
112+
.blue10, .blue20, .blue30,
113+
.blue20, .blue30, .blue40,
114+
]
115+
)
116+
.ignoresSafeArea()
117+
}
118+
119+
private var azureWaveGradient: some View {
120+
MeshGradient(
121+
width: 3,
122+
height: 3,
123+
points: [
124+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
125+
[0.0, 0.5], [0.3, 0.5], [1.0, 0.5],
126+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
127+
],
128+
colors: [
129+
.blue40, .blue50, .blue60,
130+
.blue50, .blue60, .blue70,
131+
.blue60, .blue70, .blue80,
132+
]
133+
)
134+
.ignoresSafeArea()
135+
}
136+
137+
private var midnightBlueGradient: some View {
138+
MeshGradient(
139+
width: 3,
140+
height: 3,
141+
points: [
142+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
143+
[0.0, 0.5], [0.5, 0.5], [1.0, 0.5],
144+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
145+
],
146+
colors: [
147+
.blue05, .indigo10, .blue10,
148+
.indigo10, .blue20, .indigo20,
149+
.blue10, .indigo20, .blue30,
150+
]
151+
)
152+
.ignoresSafeArea()
153+
}
154+
155+
private var crystalWatersGradient: some View {
156+
MeshGradient(
157+
width: 3,
158+
height: 3,
159+
points: [
160+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
161+
[0.0, 0.5], [0.5, 0.5], [1.0, 0.5],
162+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
163+
],
164+
colors: [
165+
.cyan50, .blue60, .cyan60,
166+
.blue60, .cyan70, .blue70,
167+
.cyan60, .blue70, .cyan80,
168+
]
169+
)
170+
.ignoresSafeArea()
171+
}
172+
173+
private var electricBlueGradient: some View {
174+
MeshGradient(
175+
width: 3,
176+
height: 3,
177+
points: [
178+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
179+
[0.0, 0.5], [0.5, 0.5], [1.0, 0.5],
180+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
181+
],
182+
colors: [
183+
.blue60, .indigo60, .blue70,
184+
.indigo60, .blue70, .indigo70,
185+
.blue70, .indigo70, .blue80,
186+
]
187+
)
188+
.ignoresSafeArea()
189+
}
190+
191+
private var sapphireGlowGradient: some View {
192+
MeshGradient(
193+
width: 3,
194+
height: 3,
195+
points: [
196+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
197+
[0.0, 0.5], [0.7, 0.5], [1.0, 0.5],
198+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
199+
],
200+
colors: [
201+
.blue30, .blue40, .blue50,
202+
.blue40, .indigo50, .blue60,
203+
.blue50, .blue60, .indigo60,
204+
]
205+
)
206+
.ignoresSafeArea()
207+
}
208+
209+
private var arcticFlowGradient: some View {
210+
MeshGradient(
211+
width: 3,
212+
height: 3,
213+
points: [
214+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
215+
[0.0, 0.5], [0.5, 0.5], [1.0, 0.5],
216+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
217+
],
218+
colors: [
219+
.blue80, .cyan80, .blue90,
220+
.cyan80, .blue90, .cyan90,
221+
.blue90, .cyan90, .blue95,
222+
]
223+
)
224+
.ignoresSafeArea()
225+
}
226+
227+
private var blueHorizonGradient: some View {
228+
MeshGradient(
229+
width: 3,
230+
height: 3,
231+
points: [
232+
[0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
233+
[0.0, 0.5], [0.5, 0.6], [1.0, 0.5],
234+
[0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
235+
],
236+
colors: [
237+
.blue50, .blue60, .cyan60,
238+
.blue60, .cyan70, .blue70,
239+
.cyan70, .blue80, .cyan80,
240+
]
241+
)
242+
.ignoresSafeArea()
243+
}
244+
}

Sources/App/WebView/ExperimentalSpace/Home/HomeViewConfiguration.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,24 @@ struct HomeViewConfiguration: Codable, FetchableRecord, PersistableRecord, Equat
1010
var allowMultipleSelection: Bool
1111
var entityOrderByRoom: [String: [String]]
1212
var hiddenEntityIds: Set<String>
13+
var selectedBackgroundId: String?
1314

1415
init(
1516
id: String,
1617
sectionOrder: [String] = [],
1718
visibleSectionIds: Set<String> = [],
1819
allowMultipleSelection: Bool = false,
1920
entityOrderByRoom: [String: [String]] = [:],
20-
hiddenEntityIds: Set<String> = []
21+
hiddenEntityIds: Set<String> = [],
22+
selectedBackgroundId: String? = nil
2123
) {
2224
self.id = id
2325
self.sectionOrder = sectionOrder
2426
self.visibleSectionIds = visibleSectionIds
2527
self.allowMultipleSelection = allowMultipleSelection
2628
self.entityOrderByRoom = entityOrderByRoom
2729
self.hiddenEntityIds = hiddenEntityIds
30+
self.selectedBackgroundId = selectedBackgroundId
2831
}
2932

3033
/// Fetch configuration for a specific server

Sources/App/WebView/ExperimentalSpace/Home/HomeViewCustomizationView.swift

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,42 @@ struct HomeViewCustomizationView: View {
1010

1111
var body: some View {
1212
NavigationStack {
13-
Text("Unavailable")
14-
.toolbar {
15-
ToolbarItem(placement: .primaryAction) {
16-
CloseButton {
17-
dismiss()
18-
}
13+
Form {
14+
Section {
15+
backgroundPicker
16+
} header: {
17+
Text("Background")
18+
} footer: {
19+
Text("Choose a background style for your home view")
20+
}
21+
}
22+
.navigationTitle("Customize")
23+
.toolbar {
24+
ToolbarItem(placement: .primaryAction) {
25+
CloseButton {
26+
dismiss()
27+
}
28+
}
29+
}
30+
}
31+
}
32+
33+
@ViewBuilder
34+
private var backgroundPicker: some View {
35+
ForEach(HomeViewBackgroundOption.allOptions) { option in
36+
Button {
37+
viewModel.configuration.selectedBackgroundId = option.id
38+
} label: {
39+
HStack {
40+
Text(option.name)
41+
.foregroundStyle(.primary)
42+
Spacer()
43+
if viewModel.configuration.selectedBackgroundId == option.id {
44+
Image(systemSymbol: .checkmark)
45+
.foregroundStyle(.haPrimary)
1946
}
2047
}
48+
}
2149
}
2250
}
2351
}

0 commit comments

Comments
 (0)