Add explicit environment objects to TableColumn (#873)
For some reason, Table doesn't seem to inherit the environment in some cases. Reapply environment to each TableColumn (only Theme is required). Work around what clearly seems to be a SwiftUI bug. Fixes #872
This commit is contained in:
parent
962ffdf678
commit
09e894dd60
|
@ -38,13 +38,12 @@ extension MigrateView {
|
|||
var body: some View {
|
||||
Section {
|
||||
ForEach(profiles, id: \.id) {
|
||||
switch step {
|
||||
case .initial, .fetching, .fetched:
|
||||
button(forProfile: $0)
|
||||
|
||||
default:
|
||||
row(forProfile: $0, status: statuses[$0.id])
|
||||
}
|
||||
ControlView(
|
||||
step: step,
|
||||
profile: $0,
|
||||
isIncluded: isIncludedBinding(for: $0.id),
|
||||
status: statusBinding(for: $0.id)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,25 +51,73 @@ extension MigrateView {
|
|||
}
|
||||
|
||||
private extension MigrateView.SectionView {
|
||||
func button(forProfile profile: MigratableProfile) -> some View {
|
||||
Button {
|
||||
if statuses[profile.id] == .excluded {
|
||||
statuses.removeValue(forKey: profile.id)
|
||||
func isIncludedBinding(for profileId: UUID) -> Binding<Bool> {
|
||||
Binding {
|
||||
statuses[profileId] != .excluded
|
||||
} set: {
|
||||
if $0 {
|
||||
statuses.removeValue(forKey: profileId)
|
||||
} else {
|
||||
statuses[profile.id] = .excluded
|
||||
statuses[profileId] = .excluded
|
||||
}
|
||||
} label: {
|
||||
row(forProfile: profile, status: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func row(forProfile profile: MigratableProfile, status: MigrationStatus?) -> some View {
|
||||
func statusBinding(for profileId: UUID) -> Binding<MigrationStatus?> {
|
||||
Binding {
|
||||
statuses[profileId]
|
||||
} set: {
|
||||
if let newValue = $0 {
|
||||
statuses[profileId] = newValue
|
||||
} else {
|
||||
statuses.removeValue(forKey: profileId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension MigrateView.SectionView {
|
||||
struct ControlView: View {
|
||||
let step: MigrateView.Model.Step
|
||||
|
||||
let profile: MigratableProfile
|
||||
|
||||
@Binding
|
||||
var isIncluded: Bool
|
||||
|
||||
@Binding
|
||||
var status: MigrationStatus?
|
||||
|
||||
var body: some View {
|
||||
switch step {
|
||||
case .initial, .fetching, .fetched:
|
||||
buttonView
|
||||
|
||||
default:
|
||||
rowView
|
||||
}
|
||||
}
|
||||
|
||||
var buttonView: some View {
|
||||
Button {
|
||||
if status == .excluded {
|
||||
status = nil
|
||||
} else {
|
||||
status = .excluded
|
||||
}
|
||||
} label: {
|
||||
rowView
|
||||
}
|
||||
}
|
||||
|
||||
var rowView: some View {
|
||||
HStack {
|
||||
CardView(profile: profile)
|
||||
Spacer()
|
||||
StatusView(isIncluded: statuses[profile.id] != .excluded, status: status)
|
||||
StatusView(isIncluded: status != .excluded, status: status)
|
||||
}
|
||||
.foregroundStyle(status.style)
|
||||
}
|
||||
.foregroundStyle(statuses[profile.id].style)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ import SwiftUI
|
|||
|
||||
extension MigrateView {
|
||||
struct TableView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var theme: Theme
|
||||
|
||||
let step: Model.Step
|
||||
|
||||
let profiles: [MigratableProfile]
|
||||
|
@ -45,17 +49,13 @@ extension MigrateView {
|
|||
Text($0.timestamp)
|
||||
.foregroundStyle(statuses.style(for: $0.id))
|
||||
}
|
||||
TableColumn("") { profile in
|
||||
switch step {
|
||||
case .initial, .fetching, .fetched:
|
||||
Toggle("", isOn: isIncludedBinding(for: profile.id))
|
||||
.labelsHidden()
|
||||
|
||||
default:
|
||||
if let status = statuses[profile.id] {
|
||||
StatusView(status: status)
|
||||
}
|
||||
}
|
||||
TableColumn("") {
|
||||
ControlView(
|
||||
step: step,
|
||||
isIncluded: isIncludedBinding(for: $0.id),
|
||||
status: statuses[$0.id]
|
||||
)
|
||||
.environmentObject(theme) // TODO: #873, Table loses environment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,10 +77,27 @@ private extension MigrateView.TableView {
|
|||
}
|
||||
|
||||
private extension MigrateView.TableView {
|
||||
struct StatusView: View {
|
||||
let status: MigrationStatus
|
||||
struct ControlView: View {
|
||||
let step: MigrateView.Model.Step
|
||||
|
||||
@Binding
|
||||
var isIncluded: Bool
|
||||
|
||||
let status: MigrationStatus?
|
||||
|
||||
var body: some View {
|
||||
switch step {
|
||||
case .initial, .fetching, .fetched:
|
||||
Toggle("", isOn: $isIncluded)
|
||||
.labelsHidden()
|
||||
|
||||
default:
|
||||
statusView
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var statusView: some View {
|
||||
switch status {
|
||||
case .excluded:
|
||||
Text("--")
|
||||
|
@ -93,6 +110,9 @@ private extension MigrateView.TableView {
|
|||
|
||||
case .failed:
|
||||
ThemeImage(.failure)
|
||||
|
||||
case .none:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@ extension VPNProviderServerView {
|
|||
|
||||
extension VPNProviderServerView {
|
||||
struct ServersSubview: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var theme: Theme
|
||||
|
||||
let servers: [VPNServer]
|
||||
|
||||
let selectedServer: VPNServer?
|
||||
|
@ -69,12 +73,14 @@ extension VPNProviderServerView {
|
|||
TableColumn("") { server in
|
||||
ThemeImage(.marked)
|
||||
.opaque(server.id == selectedServer?.id)
|
||||
.environmentObject(theme) // TODO: #873, Table loses environment
|
||||
}
|
||||
.width(10.0)
|
||||
|
||||
TableColumn(Strings.Global.region) { server in
|
||||
ThemeCountryText(server.provider.countryCode, title: server.region)
|
||||
.help(server.region)
|
||||
.environmentObject(theme) // TODO: #873, Table loses environment
|
||||
}
|
||||
|
||||
TableColumn(Strings.Global.address, value: \.address)
|
||||
|
@ -84,6 +90,7 @@ extension VPNProviderServerView {
|
|||
value: server.serverId,
|
||||
selection: $favoritesManager.serverIds
|
||||
)
|
||||
.environmentObject(theme) // TODO: #873, Table loses environment
|
||||
}
|
||||
.width(15.0)
|
||||
|
||||
|
|
|
@ -85,11 +85,11 @@ private extension AppDelegate {
|
|||
|
||||
// MARK: - Preconcurrency warnings
|
||||
|
||||
extension NSWorkspace: @unchecked Sendable {
|
||||
extension NSWorkspace: @retroactive @unchecked Sendable {
|
||||
}
|
||||
|
||||
extension NSRunningApplication: @unchecked Sendable {
|
||||
}
|
||||
|
||||
extension NSWorkspace.OpenConfiguration: @unchecked Sendable {
|
||||
extension NSWorkspace.OpenConfiguration: @retroactive @unchecked Sendable {
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue