Skip to content

Commit faa6191

Browse files
committed
fix: custom domain ui
1 parent 33e1231 commit faa6191

13 files changed

Lines changed: 191 additions & 74 deletions

‎Normal/Resources/Localizable.xcstrings‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,10 @@
707707
"comment" : "A message indicating that there are no apps available to select.",
708708
"isCommentAutoGenerated" : true
709709
},
710+
"No Apps Selected" : {
711+
"comment" : "A message indicating that a user has not selected any apps.",
712+
"isCommentAutoGenerated" : true
713+
},
710714
"No custom domains yet. Add them in App Select." : {
711715

712716
},
@@ -911,6 +915,10 @@
911915
"comment" : "The title of the view.",
912916
"isCommentAutoGenerated" : true
913917
},
918+
"Schedule" : {
919+
"comment" : "The title of the view that lists schedules.",
920+
"isCommentAutoGenerated" : true
921+
},
914922
"Schedules" : {
915923
"comment" : "The title of the screen where users can view and manage their schedules.",
916924
"isCommentAutoGenerated" : true

‎Normal/Shared/Components/CustomDomainsEditor.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ struct CustomDomainsEditor: View {
7272
.onDelete(perform: deleteAction)
7373
} footer: {
7474
if !isEditable {
75-
Text("Unblock all apps to edit custom domains.")
75+
Text(BlockedMessage.customDomains)
7676
}
7777
}
7878
}
Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,14 @@
11
import SwiftUI
22

3-
struct CustomDomainsSubsetSection: View {
4-
let available: [String]
3+
struct CustomDomainSubsetRow: View {
4+
let domain: String
55
@Binding var selected: [String]
6+
var isEditable: Bool = true
67

7-
var body: some View {
8-
Section {
9-
if available.isEmpty {
10-
Text("No custom domains yet. Add them in App Select.")
11-
.foregroundStyle(.secondary)
12-
} else {
13-
ForEach(available, id: \.self) { domain in
14-
row(for: domain)
15-
}
16-
}
17-
} header: {
18-
Text("Custom Domains")
19-
}
20-
}
8+
private var isOn: Bool { selected.contains(domain) }
219

22-
private func row(for domain: String) -> some View {
23-
let isOn = selected.contains(domain)
24-
return Button {
25-
toggle(domain)
26-
} label: {
10+
var body: some View {
11+
Button(action: toggle) {
2712
HStack(spacing: DS.Spacing.md) {
2813
Image(systemName: isOn ? "checkmark.circle.fill" : "circle")
2914
.font(.title2)
@@ -34,15 +19,81 @@ struct CustomDomainsSubsetSection: View {
3419
.contentShape(.rect)
3520
}
3621
.buttonStyle(.plain)
22+
.disabled(!isEditable)
3723
.sensoryFeedback(.selection, trigger: isOn)
3824
.accessibilityIdentifier("customDomains.row.\(domain)")
3925
}
4026

41-
private func toggle(_ domain: String) {
27+
private func toggle() {
4228
if let index = selected.firstIndex(of: domain) {
4329
selected.remove(at: index)
4430
} else {
4531
selected.append(domain)
4632
}
4733
}
4834
}
35+
36+
struct CustomDomainsSubsetSection: View {
37+
let available: [String]
38+
@Binding var selected: [String]
39+
40+
var body: some View {
41+
Section("Custom Domains") {
42+
if available.isEmpty {
43+
Text("No custom domains yet. Add them in App Select.")
44+
.foregroundStyle(.secondary)
45+
} else {
46+
ForEach(available, id: \.self) { domain in
47+
CustomDomainSubsetRow(domain: domain, selected: $selected)
48+
}
49+
}
50+
}
51+
}
52+
}
53+
54+
struct CustomDomainsSubsetLink: View {
55+
let available: [String]
56+
@Binding var selected: [String]
57+
var isEditable: Bool = true
58+
59+
private var effectiveCount: Int {
60+
CustomDomains.subset(selected, of: available).count
61+
}
62+
63+
var body: some View {
64+
NavigationLink {
65+
CustomDomainsSubsetEditor(available: available, selected: $selected, isEditable: isEditable)
66+
} label: {
67+
CountRow(title: "Custom Domains", count: effectiveCount)
68+
.opacity(isEditable ? 1 : DS.Opacity.dim)
69+
}
70+
.accessibilityIdentifier("customDomains.subsetLink")
71+
}
72+
}
73+
74+
struct CustomDomainsSubsetEditor: View {
75+
let available: [String]
76+
@Binding var selected: [String]
77+
var isEditable: Bool = true
78+
79+
var body: some View {
80+
Form {
81+
Section {
82+
if available.isEmpty {
83+
Text("No custom domains yet. Add them in App Select.")
84+
.foregroundStyle(.secondary)
85+
} else {
86+
ForEach(available, id: \.self) { domain in
87+
CustomDomainSubsetRow(domain: domain, selected: $selected, isEditable: isEditable)
88+
}
89+
}
90+
} footer: {
91+
if !isEditable {
92+
Text(BlockedMessage.customDomains)
93+
}
94+
}
95+
}
96+
.navigationTitle("Custom Domains")
97+
.navigationBarTitleDisplayMode(.inline)
98+
}
99+
}

‎Normal/Shared/Components/FooterMessage.swift‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,10 @@ struct FooterMessage: View {
1111
.padding()
1212
}
1313
}
14+
15+
enum BlockedMessage {
16+
static let groups: LocalizedStringKey = "Unblock all apps to add or edit groups."
17+
static let schedules: LocalizedStringKey = "Unblock all apps to add or edit schedules."
18+
static let keys: LocalizedStringKey = "Unblock all apps to add or delete keys."
19+
static let customDomains: LocalizedStringKey = "Unblock all apps to edit custom domains."
20+
}

‎Normal/Shared/Components/SelectionIconsView.swift‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ struct SelectionIconsView: View {
2626
}
2727
case let .domain(domain):
2828
Image(systemName: "globe")
29-
.foregroundStyle(.secondary)
29+
.foregroundStyle(.blue.opacity(0.45))
3030
.overlay {
3131
Text(domain.prefix(1).lowercased())
32-
.font(.system(size: 8, weight: .heavy))
33-
.foregroundStyle(.primary)
32+
.font(.system(size: 12, weight: .medium, design: .rounded))
33+
.foregroundStyle(.blue)
3434
}
3535
}
3636
}

‎Normal/Views/AppSelect/SelectionListView.swift‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,38 @@
11
import FamilyControls
22
import SwiftUI
33

4+
struct ViewOnlyAppsList: View {
5+
let selection: FamilyActivitySelection
6+
7+
var body: some View {
8+
List {
9+
tokenSection("Categories", tokens: selection.categoryTokens.asHashableArray)
10+
tokenSection("Apps", tokens: selection.applicationTokens.asHashableArray)
11+
tokenSection("Websites", tokens: selection.webDomainTokens.sortedStably.map { $0 as AnyHashable })
12+
}
13+
.navigationTitle("Apps")
14+
.navigationBarTitleDisplayMode(.inline)
15+
.overlay {
16+
if selection.allTokens.isEmpty {
17+
ContentUnavailableView("No Apps Selected", systemImage: "app.dashed")
18+
}
19+
}
20+
}
21+
22+
@ViewBuilder
23+
private func tokenSection(_ title: LocalizedStringKey, tokens: [AnyHashable]) -> some View {
24+
if !tokens.isEmpty {
25+
Section(title) {
26+
ForEach(tokens, id: \.self) { token in
27+
if let kind = SelectedTokenKind(token) {
28+
SelectionTokenLabel(kind: kind)
29+
}
30+
}
31+
}
32+
}
33+
}
34+
}
35+
436
struct SelectionListView: View {
537
let selection: FamilyActivitySelection
638

‎Normal/Views/Groups/GroupFormSheet.swift‎

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@ struct GroupFormSheet: View {
8383
}
8484

8585
if customDomainsEnabled {
86-
if isReadOnly {
87-
customDomainsReadOnlySection
88-
} else {
89-
CustomDomainsSubsetSection(available: availableDomains, selected: $customDomains)
86+
Section {
87+
CustomDomainsSubsetLink(
88+
available: availableDomains,
89+
selected: $customDomains,
90+
isEditable: !isReadOnly
91+
)
9092
}
9193
}
9294

@@ -103,6 +105,11 @@ struct GroupFormSheet: View {
103105
}
104106
.navigationTitle(isReadOnly ? "Group" : (isNew ? "New Group" : "Edit Group"))
105107
.navigationBarTitleDisplayMode(.inline)
108+
.safeAreaInset(edge: .bottom) {
109+
if isReadOnly {
110+
FooterMessage(text: BlockedMessage.groups)
111+
}
112+
}
106113
.toolbar {
107114
if isReadOnly {
108115
ToolbarItem(placement: .confirmationAction) {
@@ -135,27 +142,10 @@ struct GroupFormSheet: View {
135142
}
136143

137144
private var appsReadOnlyRow: some View {
138-
HStack(spacing: DS.Spacing.md) {
139-
Text("Apps")
140-
Spacer()
141-
if selection.count > 0 {
142-
SelectionIconsView(tokens: selection.allTokens, limit: 5)
143-
} else {
144-
Text("None").foregroundStyle(.secondary)
145-
}
146-
}
147-
}
148-
149-
@ViewBuilder
150-
private var customDomainsReadOnlySection: some View {
151-
Section("Custom Domains") {
152-
if effectiveCustomDomains.isEmpty {
153-
Text("None").foregroundStyle(.secondary)
154-
} else {
155-
ForEach(effectiveCustomDomains, id: \.self) { domain in
156-
Label(domain, systemImage: "globe").lineLimit(1)
157-
}
158-
}
145+
NavigationLink {
146+
ViewOnlyAppsList(selection: selection)
147+
} label: {
148+
CountRow(title: "Apps", count: selection.count)
159149
}
160150
}
161151

‎Normal/Views/Groups/GroupsView.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct GroupsView: View {
4949
}, onMove: move)
5050
.safeAreaInset(edge: .bottom) {
5151
if isBlocked {
52-
FooterMessage(text: "Unblock all apps to add or edit groups.")
52+
FooterMessage(text: BlockedMessage.groups)
5353
} else if !hasGlobalKey {
5454
FooterMessage(text: "Add a key in the Keys tab before blocking groups.")
5555
}

‎Normal/Views/Key/KeyFormSheet.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ struct KeyFormSheet: View {
127127

128128
if isReadOnly {
129129
Section {} footer: {
130-
Text("Unblock all apps to edit or delete keys.")
130+
Text(BlockedMessage.keys)
131131
.frame(maxWidth: .infinity, alignment: .center)
132132
}
133133
}

‎Normal/Views/Key/KeysView.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct KeysView: View {
4949
}
5050
.safeAreaInset(edge: .bottom) {
5151
if isBlocked {
52-
FooterMessage(text: "Unblock all apps to edit or delete keys.")
52+
FooterMessage(text: BlockedMessage.keys)
5353
} else if keys.isEmpty, !groupKeys.isEmpty {
5454
groupKeysLink
5555
.padding(.horizontal, DS.Spacing.lg)

0 commit comments

Comments
 (0)