fix: avoid Swift compiler crash in onboarding wizard
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import Observation
|
||||
import SwiftUI
|
||||
|
||||
extension OnboardingView {
|
||||
@@ -13,43 +14,10 @@ extension OnboardingView {
|
||||
.frame(maxWidth: 520)
|
||||
|
||||
self.onboardingCard(spacing: 14, padding: 16) {
|
||||
if let error = self.onboardingWizard.errorMessage {
|
||||
Text("Wizard error")
|
||||
.font(.headline)
|
||||
Text(error)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Button("Retry") {
|
||||
self.onboardingWizard.reset()
|
||||
Task {
|
||||
await self.onboardingWizard.startIfNeeded(
|
||||
mode: self.state.connectionMode,
|
||||
workspace: self.workspacePath.isEmpty ? nil : self.workspacePath)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
} else if self.onboardingWizard.isStarting {
|
||||
HStack(spacing: 8) {
|
||||
ProgressView()
|
||||
Text("Starting wizard…")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
} else if let step = self.onboardingWizard.currentStep {
|
||||
OnboardingWizardStepView(
|
||||
step: step,
|
||||
isSubmitting: self.onboardingWizard.isSubmitting)
|
||||
{ value in
|
||||
Task { await self.onboardingWizard.submit(step: step, value: value) }
|
||||
}
|
||||
.id(step.id)
|
||||
} else if self.onboardingWizard.isComplete {
|
||||
Text("Wizard complete. Continue to the next step.")
|
||||
.font(.headline)
|
||||
} else {
|
||||
Text("Waiting for wizard…")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
OnboardingWizardCardContent(
|
||||
wizard: self.onboardingWizard,
|
||||
mode: self.state.connectionMode,
|
||||
workspacePath: self.workspacePath)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
@@ -60,3 +28,66 @@ extension OnboardingView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct OnboardingWizardCardContent: View {
|
||||
@Bindable var wizard: OnboardingWizardModel
|
||||
let mode: AppState.ConnectionMode
|
||||
let workspacePath: String
|
||||
|
||||
private enum CardState {
|
||||
case error(String)
|
||||
case starting
|
||||
case step(WizardStep)
|
||||
case complete
|
||||
case waiting
|
||||
}
|
||||
|
||||
private var state: CardState {
|
||||
if let error = wizard.errorMessage { return .error(error) }
|
||||
if wizard.isStarting { return .starting }
|
||||
if let step = wizard.currentStep { return .step(step) }
|
||||
if wizard.isComplete { return .complete }
|
||||
return .waiting
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
switch state {
|
||||
case .error(let error):
|
||||
Text("Wizard error")
|
||||
.font(.headline)
|
||||
Text(error)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Button("Retry") {
|
||||
wizard.reset()
|
||||
Task {
|
||||
await wizard.startIfNeeded(
|
||||
mode: mode,
|
||||
workspace: workspacePath.isEmpty ? nil : workspacePath)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
case .starting:
|
||||
HStack(spacing: 8) {
|
||||
ProgressView()
|
||||
Text("Starting wizard…")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
case .step(let step):
|
||||
OnboardingWizardStepView(
|
||||
step: step,
|
||||
isSubmitting: wizard.isSubmitting)
|
||||
{ value in
|
||||
Task { await wizard.submit(step: step, value: value) }
|
||||
}
|
||||
.id(step.id)
|
||||
case .complete:
|
||||
Text("Wizard complete. Continue to the next step.")
|
||||
.font(.headline)
|
||||
case .waiting:
|
||||
Text("Waiting for wizard…")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,20 +196,18 @@ struct OnboardingWizardStepView: View {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var textField: some View {
|
||||
let isSensitive = step.sensitive == true
|
||||
if isSensitive {
|
||||
return AnyView(
|
||||
SecureField(step.placeholder ?? "", text: $textValue)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.frame(maxWidth: 360)
|
||||
)
|
||||
}
|
||||
return AnyView(
|
||||
SecureField(step.placeholder ?? "", text: $textValue)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.frame(maxWidth: 360)
|
||||
} else {
|
||||
TextField(step.placeholder ?? "", text: $textValue)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.frame(maxWidth: 360)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var selectOptions: some View {
|
||||
@@ -240,15 +238,7 @@ struct OnboardingWizardStepView: View {
|
||||
private var multiselectOptions: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
ForEach(optionItems) { item in
|
||||
Toggle(isOn: Binding(get: {
|
||||
selectedIndices.contains(item.index)
|
||||
}, set: { newValue in
|
||||
if newValue {
|
||||
selectedIndices.insert(item.index)
|
||||
} else {
|
||||
selectedIndices.remove(item.index)
|
||||
}
|
||||
})) {
|
||||
Toggle(isOn: bindingForOption(item)) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(item.option.label)
|
||||
if let hint = item.option.hint, !hint.isEmpty {
|
||||
@@ -262,6 +252,18 @@ struct OnboardingWizardStepView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func bindingForOption(_ item: WizardOptionItem) -> Binding<Bool> {
|
||||
Binding(get: {
|
||||
selectedIndices.contains(item.index)
|
||||
}, set: { newValue in
|
||||
if newValue {
|
||||
selectedIndices.insert(item.index)
|
||||
} else {
|
||||
selectedIndices.remove(item.index)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private var isBlocked: Bool {
|
||||
let type = wizardStepType(step)
|
||||
if type == "select" { return optionItems.isEmpty }
|
||||
|
||||
Reference in New Issue
Block a user