Compare commits

..

2 Commits

Author SHA1 Message Date
Shadowfacts f5e9f66f76 Fix app background colors not updating when preference changed
This only fully fixes it on iOS 17, but it seems to be the best we can do
2024-04-16 12:03:52 -04:00
Shadowfacts ee5f9a62ff Fix push subscription settings GroupBox background in dark mode
Closes #470
2024-04-16 11:37:36 -04:00
5 changed files with 117 additions and 21 deletions

View File

@ -74,4 +74,9 @@ extension PreferenceStore {
public func hasFeatureFlag(_ flag: FeatureFlag) -> Bool { public func hasFeatureFlag(_ flag: FeatureFlag) -> Bool {
enabledFeatureFlags.contains(flag) enabledFeatureFlags.contains(flag)
} }
public func getValue<Key: PreferenceKey>(preferenceKeyPath: KeyPath<PreferenceStore, PreferencePublisher<Key>>) -> Key.Value {
self[keyPath: preferenceKeyPath].preference.wrappedValue
}
} }

View File

@ -8,26 +8,11 @@
import SwiftUI import SwiftUI
import Combine import Combine
import TuskerPreferences
extension View { extension View {
@MainActor func appGroupedListBackground(container: UIAppearanceContainer.Type) -> some View {
@ViewBuilder self.modifier(AppGroupedListBackground(container: container))
func appGroupedListBackground(container: UIAppearanceContainer.Type, applyBackground: Bool = true) -> some View {
if #available(iOS 16.0, *) {
if applyBackground {
self
.scrollContentBackground(.hidden)
.background(Color.appGroupedBackground.edgesIgnoringSafeArea(.all))
} else {
self
.scrollContentBackground(.hidden)
}
} else {
self
.onAppear {
UITableView.appearance(whenContainedInInstancesOf: [container]).backgroundColor = .appGroupedBackground
}
}
} }
func appGroupedListRowBackground() -> some View { func appGroupedListRowBackground() -> some View {
@ -35,11 +20,45 @@ extension View {
} }
} }
private struct AppGroupedListRowBackground: ViewModifier { private struct AppGroupedListBackground: ViewModifier {
let container: any UIAppearanceContainer.Type
@Environment(\.colorScheme) private var colorScheme @Environment(\.colorScheme) private var colorScheme
@Environment(\.pureBlackDarkMode) private var environmentPureBlackDarkMode
private var pureBlackDarkMode: Bool {
// using @PreferenceObserving just does not work for this, so try the environment key when available
// if it's not available, the color won't update automatically, but it will be correct when the view is created
if #available(iOS 17.0, *) {
environmentPureBlackDarkMode
} else {
false
}
}
func body(content: Content) -> some View { func body(content: Content) -> some View {
if colorScheme == .dark, !Preferences.shared.pureBlackDarkMode { if #available(iOS 16.0, *) {
if colorScheme == .dark, !pureBlackDarkMode {
content
.scrollContentBackground(.hidden)
.background(Color.appGroupedBackground.edgesIgnoringSafeArea(.all))
} else {
content
}
} else {
content
.onAppear {
UITableView.appearance(whenContainedInInstancesOf: [container]).backgroundColor = .appGroupedBackground
}
}
}
}
private struct AppGroupedListRowBackground: ViewModifier {
@Environment(\.colorScheme) private var colorScheme
@PreferenceObserving(\.$pureBlackDarkMode) private var pureBlackDarkMode
func body(content: Content) -> some View {
if colorScheme == .dark, !pureBlackDarkMode {
content content
.listRowBackground(Color.appGroupedCellBackground) .listRowBackground(Color.appGroupedCellBackground)
} else { } else {
@ -47,3 +66,31 @@ private struct AppGroupedListRowBackground: ViewModifier {
} }
} }
} }
@propertyWrapper
private struct PreferenceObserving<Key: TuskerPreferences.PreferenceKey>: DynamicProperty {
typealias PrefKeyPath = KeyPath<PreferenceStore, PreferencePublisher<Key>>
let keyPath: PrefKeyPath
@StateObject private var observer: Observer
init(_ keyPath: PrefKeyPath) {
self.keyPath = keyPath
self._observer = StateObject(wrappedValue: Observer(keyPath: keyPath))
}
var wrappedValue: Key.Value {
Preferences.shared.getValue(preferenceKeyPath: keyPath)
}
@MainActor
private class Observer: ObservableObject {
private var cancellable: AnyCancellable?
init(keyPath: PrefKeyPath) {
cancellable = Preferences.shared[keyPath: keyPath].sink { [unowned self] _ in
self.objectWillChange.send()
}
}
}
}

View File

@ -143,3 +143,33 @@ extension UIMutableTraits {
set { self[PureBlackDarkModeTrait.self] = newValue } set { self[PureBlackDarkModeTrait.self] = newValue }
} }
} }
@available(iOS 17.0, *)
private struct PureBlackDarkModeKey: UITraitBridgedEnvironmentKey {
static let defaultValue: Bool = false
static func read(from traitCollection: UITraitCollection) -> Bool {
traitCollection[PureBlackDarkModeTrait.self]
}
static func write(to mutableTraits: inout any UIMutableTraits, value: Bool) {
mutableTraits[PureBlackDarkModeTrait.self] = value
}
}
extension EnvironmentValues {
var pureBlackDarkMode: Bool {
get {
if #available(iOS 17.0, *) {
self[PureBlackDarkModeKey.self]
} else {
true
}
}
set {
if #available(iOS 17.0, *) {
self[PureBlackDarkModeKey.self] = newValue
}
}
}
}

View File

@ -77,6 +77,7 @@ private struct PushSubscriptionSettingsView: View {
// status notifications not supported until we can enable/disable them in the app // status notifications not supported until we can enable/disable them in the app
} }
} }
.groupBoxStyle(AppBackgroundGroupBoxStyle())
} }
private var allSupportedAlertTypes: PushSubscription.Alerts { private var allSupportedAlertTypes: PushSubscription.Alerts {
@ -125,6 +126,19 @@ private extension PushSubscription.Policy {
} }
} }
private struct AppBackgroundGroupBoxStyle: GroupBoxStyle {
func makeBody(configuration: Configuration) -> some View {
VStack(alignment: .leading, spacing: 16) {
configuration.label
.font(.headline)
configuration.content
}
.padding()
.background(Color.appGroupedBackground, in: RoundedRectangle(cornerRadius: 8))
}
}
//#Preview { //#Preview {
// PushSubscriptionView() // PushSubscriptionView()
//} //}

View File

@ -157,7 +157,7 @@ struct ReportView: View {
.appGroupedListRowBackground() .appGroupedListRowBackground()
} }
.listStyle(.insetGrouped) .listStyle(.insetGrouped)
.appGroupedListBackground(container: UIHostingController<ReportView>.self, applyBackground: true) .appGroupedListBackground(container: UIHostingController<ReportView>.self)
.alertWithData("Error Reporting", data: $error, actions: { error in .alertWithData("Error Reporting", data: $error, actions: { error in
Button("OK") {} Button("OK") {}
}, message: { error in }, message: { error in