Vorher kein Weg aus dem Wizard außer schrittweise "Zurück" bis zu Modus. Zentral als Toolbar-Button in SetupView (nicht pro Schritt dupliziert), mit Bestätigungsdialog gegen Datenverlust, deaktiviert während eines laufenden Apply. SetupViewModel.finish() in gemeinsame resetToInitialState() plus benannte finish()/cancel()-Wrapper aufgeteilt. Live bestätigt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
82 lines
3.5 KiB
Swift
82 lines
3.5 KiB
Swift
import SwiftUI
|
|
|
|
struct SetupView: View {
|
|
@ObservedObject var connectionService: ConnectionService
|
|
@StateObject private var viewModel: SetupViewModel
|
|
@AppStorage("appLanguage") private var appLanguage: String = "de"
|
|
@State private var showCancelConfirmation = false
|
|
|
|
init(connectionService: ConnectionService) {
|
|
self.connectionService = connectionService
|
|
_viewModel = StateObject(wrappedValue: SetupViewModel(connectionService: connectionService))
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Group {
|
|
if connectionService.credentials == nil {
|
|
ContentUnavailableView(
|
|
LocalizedStringKey(L10n.t("Nicht verbunden", appLanguage)),
|
|
systemImage: "network.slash",
|
|
description: Text(L10n.t("Verbinde dich zuerst im Tab \"Verbinden\" mit deinem Router.", appLanguage))
|
|
)
|
|
} else {
|
|
switch viewModel.step {
|
|
case .mode:
|
|
ModeStepView(viewModel: viewModel)
|
|
case .wan:
|
|
WanStepView(viewModel: viewModel, availableInterfaces: configurableInterfaces)
|
|
case .lan:
|
|
LanStepView(viewModel: viewModel, availableInterfaces: configurableInterfaces)
|
|
case .vlan:
|
|
VlanStepView(viewModel: viewModel, availableInterfaces: configurableInterfaces)
|
|
case .wifi:
|
|
WifiStepView(viewModel: viewModel)
|
|
case .firewall:
|
|
FirewallStepView(viewModel: viewModel)
|
|
case .review:
|
|
ReviewApplyView(viewModel: viewModel, credentials: connectionService.credentials)
|
|
}
|
|
}
|
|
}
|
|
.toolbar {
|
|
if connectionService.credentials != nil {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button(L10n.t("Abbrechen", appLanguage)) {
|
|
showCancelConfirmation = true
|
|
}
|
|
.disabled(viewModel.isApplying)
|
|
}
|
|
}
|
|
}
|
|
.confirmationDialog(
|
|
L10n.t("Einrichten abbrechen?", appLanguage),
|
|
isPresented: $showCancelConfirmation,
|
|
titleVisibility: .visible
|
|
) {
|
|
Button(L10n.t("Abbrechen", appLanguage), role: .destructive) {
|
|
viewModel.cancel()
|
|
}
|
|
Button(L10n.t("Weiter ausfüllen", appLanguage), role: .cancel) {}
|
|
} message: {
|
|
Text(L10n.t("Alle in diesem Assistenten eingegebenen Werte gehen verloren.", appLanguage))
|
|
}
|
|
}
|
|
.onAppear {
|
|
viewModel.prepareDefaults(from: configurableInterfaces)
|
|
}
|
|
}
|
|
|
|
/// Loopback is never something a person should pick as WAN/LAN/VLAN base interface —
|
|
/// excluded here (not just via defaults) so a parsing hiccup elsewhere can't offer it as
|
|
/// a selectable option, which on a real hEX device caused every WAN/firewall command to
|
|
/// silently reference "lo" instead of the real WAN port. Found via live testing.
|
|
private var configurableInterfaces: [NetworkInterface] {
|
|
connectionService.interfaces.filter { $0.type.lowercased() != "loopback" }
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
SetupView(connectionService: ConnectionService())
|
|
}
|