diff options
| author | Christian Cleberg <[email protected]> | 2026-04-13 19:06:54 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-13 19:06:54 -0500 |
| commit | a6ad2b9815d72575206bdca860eef9db850cdf1c (patch) | |
| tree | b4d5f27aa1655e5e40bab178550d6e2576d7df8b /Hutch/Views/Tickets | |
| parent | 02a44d115b3784231cad640e992d9c18a7f67b40 (diff) | |
| download | hutch-a6ad2b9815d72575206bdca860eef9db850cdf1c.tar.gz hutch-a6ad2b9815d72575206bdca860eef9db850cdf1c.tar.bz2 hutch-a6ad2b9815d72575206bdca860eef9db850cdf1c.zip | |
feat(theme): AMOLED true-black rows via themedRow()
Apply listRowBackground(Color.black) per row in AMOLED mode using ThemedRowStyle
and themedRow() across Lists and Forms. Use themedList() for scroll/grouped
backgrounds. Treat segmented and clear list rows with isAMOLED-aware
listRowBackground where Color.clear was required. Removes reliance on UIKit
appearance for list cells on iOS 16+.
Implements: https://todo.sr.ht/~ccleberg/hutch/24
Diffstat (limited to 'Hutch/Views/Tickets')
| -rw-r--r-- | Hutch/Views/Tickets/TicketDetailView.swift | 13 | ||||
| -rw-r--r-- | Hutch/Views/Tickets/TicketListView.swift | 23 | ||||
| -rw-r--r-- | Hutch/Views/Tickets/TrackerListView.swift | 3 | ||||
| -rw-r--r-- | Hutch/Views/Tickets/TrackerManagementView.swift | 37 |
4 files changed, 75 insertions, 1 deletions
diff --git a/Hutch/Views/Tickets/TicketDetailView.swift b/Hutch/Views/Tickets/TicketDetailView.swift index 32ef89c..0028083 100644 --- a/Hutch/Views/Tickets/TicketDetailView.swift +++ b/Hutch/Views/Tickets/TicketDetailView.swift @@ -739,8 +739,10 @@ private struct ResolveSheet: View { } .pickerStyle(.inline) .labelsHidden() + .themedRow() } } + .themedList() .navigationTitle("Resolve Ticket") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -801,6 +803,7 @@ private struct AssignSheet: View { .buttonStyle(.plain) } } + .themedRow() } } @@ -809,6 +812,7 @@ private struct AssignSheet: View { .textContentType(.username) .autocorrectionDisabled() .textInputAutocapitalization(.never) + .themedRow() Button("Add Assignee") { let name = username.trimmingCharacters(in: .whitespacesAndNewlines) @@ -822,8 +826,10 @@ private struct AssignSheet: View { username.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || viewModel.isPerformingAction ) + .themedRow() } } + .themedList() .navigationTitle("Assignees") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -877,7 +883,9 @@ private struct LabelsSheet: View { } } } + .themedRow() } + .themedList() } } .navigationTitle("Labels") @@ -917,11 +925,14 @@ private struct CreateLabelSheet: View { Section("Label Details") { TextField("Label name", text: $labelName) .autocorrectionDisabled() + .themedRow() } Section("Colors") { ColorPicker("Background color", selection: $backgroundColor, supportsOpacity: false) + .themedRow() ColorPicker("Text color", selection: $foregroundColor, supportsOpacity: false) + .themedRow() } Section("Preview") { @@ -936,8 +947,10 @@ private struct CreateLabelSheet: View { .clipShape(Capsule()) Spacer() } + .themedRow() } } + .themedList() .navigationTitle("New Label") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/Hutch/Views/Tickets/TicketListView.swift b/Hutch/Views/Tickets/TicketListView.swift index e58d194..940a282 100644 --- a/Hutch/Views/Tickets/TicketListView.swift +++ b/Hutch/Views/Tickets/TicketListView.swift @@ -6,6 +6,7 @@ struct TicketListView: View { @AppStorage(AppStorageKeys.swipeActionsEnabled, store: .standard) private var swipeActionsEnabled = true @Environment(AppState.self) private var appState + @Environment(\.isAMOLEDTheme) private var isAMOLED @Environment(\.dismiss) private var dismiss @Environment(\.openURL) private var openURL @State private var tracker: TrackerSummary @@ -277,7 +278,7 @@ struct TicketListView: View { vm.deleteSavedFilter(savedFilter) } .listRowInsets(EdgeInsets()) - .listRowBackground(Color.clear) + .listRowBackground(isAMOLED ? Color.black : Color.clear) .listRowSeparator(.hidden) ForEach(viewModel.filteredTickets) { ticket in @@ -344,6 +345,7 @@ struct TicketListView: View { await viewModel.loadMoreIfNeeded(currentItem: ticket) } } + .themedRow() if viewModel.isLoadingMore { HStack { @@ -352,9 +354,11 @@ struct TicketListView: View { Spacer() } .listRowSeparator(.hidden) + .themedRow() } } } + .themedList() .listStyle(.plain) .listSectionSpacing(.compact) .searchable( @@ -636,10 +640,13 @@ private struct CreateTicketSheet: View { Form { Section("Ticket Details") { TextField("Title", text: $subject) + .themedRow() TextField("Description (optional)", text: $descriptionText, axis: .vertical) .lineLimit(6...12) + .themedRow() } } + .themedList() .navigationTitle("New Ticket") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -705,7 +712,9 @@ private struct TicketLabelsSheet: View { } } } + .themedRow() } + .themedList() } } else { ContentUnavailableView( @@ -921,6 +930,7 @@ private struct TicketFilterLabelsSheet: View { } } .padding(.vertical, 4) + .themedRow() } } @@ -940,8 +950,10 @@ private struct TicketFilterLabelsSheet: View { } .buttonStyle(.plain) } + .themedRow() } } + .themedList() } } .navigationTitle("Filter Labels") @@ -982,8 +994,10 @@ private struct SaveTicketFilterSheet: View { Section("Name") { TextField("Filter name", text: $name) .textInputAutocapitalization(.words) + .themedRow() } } + .themedList() .navigationTitle("Save Filter") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -1085,6 +1099,7 @@ private struct BulkResolveSheet: View { Text("\(viewModel.selectedTicketCount) ticket\(viewModel.selectedTicketCount == 1 ? "" : "s") selected") .font(.subheadline) .foregroundStyle(.secondary) + .themedRow() } Section("Resolution") { @@ -1095,8 +1110,10 @@ private struct BulkResolveSheet: View { } .pickerStyle(.inline) .labelsHidden() + .themedRow() } } + .themedList() .navigationTitle("Close Tickets") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -1133,6 +1150,7 @@ private struct BulkAssignSheet: View { Text("\(viewModel.selectedTicketCount) ticket\(viewModel.selectedTicketCount == 1 ? "" : "s") selected") .font(.subheadline) .foregroundStyle(.secondary) + .themedRow() } if let currentUser { @@ -1146,6 +1164,7 @@ private struct BulkAssignSheet: View { } } .disabled(viewModel.selectedTicketCount == 0 || viewModel.isPerformingAction) + .themedRow() } } @@ -1154,8 +1173,10 @@ private struct BulkAssignSheet: View { .textContentType(.username) .autocorrectionDisabled() .textInputAutocapitalization(.never) + .themedRow() } } + .themedList() .navigationTitle("Assign Tickets") .navigationBarTitleDisplayMode(.inline) .toolbar { diff --git a/Hutch/Views/Tickets/TrackerListView.swift b/Hutch/Views/Tickets/TrackerListView.swift index db0fdf2..9ca3340 100644 --- a/Hutch/Views/Tickets/TrackerListView.swift +++ b/Hutch/Views/Tickets/TrackerListView.swift @@ -162,6 +162,7 @@ struct TrackerListView: View { await viewModel.loadMoreIfNeeded(currentItem: tracker) } } + .themedRow() if viewModel.isLoadingMore { HStack { @@ -170,8 +171,10 @@ struct TrackerListView: View { Spacer() } .listRowSeparator(.hidden) + .themedRow() } } + .themedList() .listStyle(.plain) .searchable( text: $vm.searchText, diff --git a/Hutch/Views/Tickets/TrackerManagementView.swift b/Hutch/Views/Tickets/TrackerManagementView.swift index cf923b6..4e04bbd 100644 --- a/Hutch/Views/Tickets/TrackerManagementView.swift +++ b/Hutch/Views/Tickets/TrackerManagementView.swift @@ -654,22 +654,27 @@ struct TrackerEditorSheet: View { TextField("Tracker name", text: $name) .textInputAutocapitalization(.never) .autocorrectionDisabled() + .themedRow() TextField("Short description (optional)", text: $description, axis: .vertical) .lineLimit(2...4) + .themedRow() Picker("Visibility", selection: $visibility) { Text("Public").tag(Visibility.public) Text("Unlisted").tag(Visibility.unlisted) Text("Private").tag(Visibility.private) } + .themedRow() } if let error, !error.isEmpty { Section { Text(error) .foregroundStyle(.red) + .themedRow() } } } + .themedList() .navigationTitle(title) .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -718,10 +723,12 @@ struct TrackerACLManagementSheet: View { List { Section("Default Access") { TrackerPermissionSummary(permissions: viewModel.defaultACL.permissions) + .themedRow() Button("Update Default ACL") { editingDefaultACL = true } .disabled(viewModel.isSavingACL) + .themedRow() } Section { @@ -731,9 +738,11 @@ struct TrackerACLManagementSheet: View { ProgressView() Spacer() } + .themedRow() } else if viewModel.acls.isEmpty { Text("No tracker-specific ACLs yet.") .foregroundStyle(.secondary) + .themedRow() } else { ForEach(viewModel.acls) { entry in VStack(alignment: .leading, spacing: 6) { @@ -756,6 +765,7 @@ struct TrackerACLManagementSheet: View { .tint(.blue) } } + .themedRow() } } header: { Text("User ACLs") @@ -763,6 +773,7 @@ struct TrackerACLManagementSheet: View { Text("Each ACL must include all five permission flags.") } } + .themedList() .navigationTitle("ACLs") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -908,6 +919,7 @@ private struct TrackerACLEditorSheet: View { TextField("Username or ~username", text: $username) .autocorrectionDisabled() .textInputAutocapitalization(.never) + .themedRow() } permissionSection @@ -916,9 +928,11 @@ private struct TrackerACLEditorSheet: View { Section { Text(error) .foregroundStyle(.red) + .themedRow() } } } + .themedList() .navigationTitle(title) .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -950,10 +964,15 @@ private struct TrackerACLEditorSheet: View { private var permissionSection: some View { Section("Permissions") { Toggle("Browse", isOn: $browse) + .themedRow() Toggle("Submit", isOn: $submit) + .themedRow() Toggle("Comment", isOn: $comment) + .themedRow() Toggle("Edit", isOn: $edit) + .themedRow() Toggle("Triage", isOn: $triage) + .themedRow() } } @@ -1003,19 +1022,26 @@ private struct TrackerDefaultACLEditorSheet: View { Form { Section("Permissions") { Toggle("Browse", isOn: $browse) + .themedRow() Toggle("Submit", isOn: $submit) + .themedRow() Toggle("Comment", isOn: $comment) + .themedRow() Toggle("Edit", isOn: $edit) + .themedRow() Toggle("Triage", isOn: $triage) + .themedRow() } if let error, !error.isEmpty { Section { Text(error) .foregroundStyle(.red) + .themedRow() } } } + .themedList() .navigationTitle("Default ACL") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -1073,6 +1099,7 @@ struct TrackerLabelManagementSheet: View { Text("Labels are managed here and reused throughout the tracker.") .font(.footnote) .foregroundStyle(.secondary) + .themedRow() } if viewModel.isLoadingLabels { @@ -1081,12 +1108,14 @@ struct TrackerLabelManagementSheet: View { ProgressView() Spacer() } + .themedRow() } else if viewModel.labels.isEmpty { ContentUnavailableView( "No Labels", systemImage: "tag", description: Text("Create labels for triage and organization.") ) + .themedRow() } else { ForEach(viewModel.labels) { label in Button { @@ -1110,8 +1139,10 @@ struct TrackerLabelManagementSheet: View { } } } + .themedRow() } } + .themedList() .navigationTitle("Labels") .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -1261,8 +1292,11 @@ private struct TrackerLabelEditorSheet: View { Form { Section("Details") { TextField("Label name", text: $name) + .themedRow() ColorPicker("Foreground", selection: $foregroundColor, supportsOpacity: false) + .themedRow() ColorPicker("Background", selection: $backgroundColor, supportsOpacity: false) + .themedRow() } Section("Preview") { @@ -1274,15 +1308,18 @@ private struct TrackerLabelEditorSheet: View { foregroundColor: foregroundColor.hexString ) ) + .themedRow() } if let error, !error.isEmpty { Section { Text(error) .foregroundStyle(.red) + .themedRow() } } } + .themedList() .navigationTitle(title) .navigationBarTitleDisplayMode(.inline) .toolbar { |
