Files
yattee/Yattee/Views/Settings/AddSource/AddWebDAVView.swift
2026-02-08 18:33:56 +01:00

228 lines
6.7 KiB
Swift

//
// AddWebDAVView.swift
// Yattee
//
// View for adding a WebDAV share as a media source.
//
import SwiftUI
struct AddWebDAVView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.appEnvironment) private var appEnvironment
// MARK: - State
@State private var name = ""
@State private var urlString = ""
@State private var username = ""
@State private var password = ""
@State private var allowInvalidCertificates = false
@State private var isTesting = false
@State private var testResult: SourceTestResult?
@State private var testProgress: String?
// Pre-filled from network discovery
var prefillURL: URL?
var prefillName: String?
var prefillAllowInvalidCertificates: Bool = false
// Closure to dismiss the parent sheet
var dismissSheet: DismissAction?
// MARK: - Computed Properties
private var canAdd: Bool {
!name.isEmpty && !urlString.isEmpty && URL(string: urlString) != nil
}
// MARK: - Body
var body: some View {
Form {
nameSection
serverSection
authSection
securitySection
if let result = testResult {
SourceTestResultSection(result: result)
}
actionSection
}
#if os(iOS)
.scrollDismissesKeyboard(.interactively)
#endif
.navigationTitle(String(localized: "sources.addWebDAV"))
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.onAppear {
if let url = prefillURL {
urlString = url.absoluteString
}
if let prefillName, name.isEmpty {
name = prefillName
}
if prefillAllowInvalidCertificates {
allowInvalidCertificates = true
}
}
}
// MARK: - Sections
private var nameSection: some View {
Section {
#if os(tvOS)
TVSettingsTextField(title: String(localized: "sources.field.name"), text: $name)
#else
TextField(String(localized: "sources.field.name"), text: $name)
#endif
} footer: {
Text(String(localized: "sources.footer.displayName"))
}
}
private var serverSection: some View {
Section {
#if os(tvOS)
TVSettingsTextField(title: String(localized: "sources.placeholder.webdavUrl"), text: $urlString)
#else
TextField(String(localized: "sources.placeholder.webdavUrl"), text: $urlString)
.textContentType(.URL)
#if os(iOS)
.keyboardType(.URL)
.textInputAutocapitalization(.never)
#endif
.autocorrectionDisabled()
#endif
} footer: {
Text(String(localized: "sources.footer.webdav"))
}
}
private var authSection: some View {
Section {
#if os(tvOS)
TVSettingsTextField(title: String(localized: "sources.field.usernameOptional"), text: $username)
TVSettingsTextField(title: String(localized: "sources.field.passwordOptional"), text: $password, isSecure: true)
#else
TextField(String(localized: "sources.field.usernameOptional"), text: $username)
.textContentType(.username)
#if os(iOS)
.textInputAutocapitalization(.never)
#endif
.autocorrectionDisabled()
SecureField(String(localized: "sources.field.passwordOptional"), text: $password)
.textContentType(.password)
#endif
} header: {
Text(String(localized: "sources.header.auth"))
} footer: {
Text(String(localized: "sources.footer.auth"))
}
}
private var securitySection: some View {
Section {
#if os(tvOS)
TVSettingsToggle(
title: String(localized: "sources.field.allowInvalidCertificates"),
isOn: $allowInvalidCertificates
)
#else
Toggle(String(localized: "sources.field.allowInvalidCertificates"), isOn: $allowInvalidCertificates)
#endif
} header: {
Text(String(localized: "sources.header.security"))
} footer: {
Text(String(localized: "sources.footer.allowInvalidCertificates"))
}
}
private var actionSection: some View {
Section {
Button {
addSource()
} label: {
if isTesting {
HStack {
ProgressView()
.controlSize(.small)
Text(testProgress ?? String(localized: "sources.testing"))
}
} else {
Text(String(localized: "sources.addSource"))
}
}
.disabled(!canAdd || isTesting)
#if os(tvOS)
.buttonStyle(TVSettingsButtonStyle())
#endif
}
}
// MARK: - Actions
private func addSource() {
guard let appEnvironment,
let url = URL(string: urlString) else { return }
isTesting = true
testResult = nil
testProgress = String(localized: "sources.testing.connecting")
let source = MediaSource.webdav(
name: name,
url: url,
username: username.isEmpty ? nil : username,
allowInvalidCertificates: allowInvalidCertificates
)
let webDAVClient = appEnvironment.webDAVClientFactory.createClient(for: source)
Task {
do {
_ = try await webDAVClient.testConnection(
source: source,
password: password.isEmpty ? nil : password
)
await MainActor.run {
if !password.isEmpty {
appEnvironment.mediaSourcesManager.setPassword(password, for: source)
}
appEnvironment.mediaSourcesManager.add(source)
isTesting = false
testProgress = nil
if let dismissSheet {
dismissSheet()
} else {
dismiss()
}
}
} catch {
await MainActor.run {
isTesting = false
testProgress = nil
testResult = .failure(error.localizedDescription)
}
}
}
}
}
// MARK: - Preview
#Preview {
NavigationStack {
AddWebDAVView()
.appEnvironment(.preview)
}
}