mirror of
https://github.com/yattee/yattee.git
synced 2026-06-05 06:14:18 +00:00
Use grouped Form style with LabeledContent rows and move primary actions into the sheet toolbar for SMB, WebDAV, Local Folder, Remote Server and the Edit sheet. iOS and tvOS branches unchanged.
325 lines
10 KiB
Swift
325 lines
10 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 {
|
|
#if os(macOS)
|
|
macOSBody
|
|
#else
|
|
Form {
|
|
nameSection
|
|
serverSection
|
|
authSection
|
|
securitySection
|
|
|
|
if let result = testResult {
|
|
SourceTestResultSection(result: result)
|
|
}
|
|
|
|
actionSection
|
|
}
|
|
#if os(iOS)
|
|
.scrollDismissesKeyboard(.interactively)
|
|
#endif
|
|
#if !os(tvOS)
|
|
.navigationTitle(String(localized: "sources.addWebDAV"))
|
|
#if os(iOS)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
#endif
|
|
#endif
|
|
.onAppear {
|
|
if let url = prefillURL {
|
|
urlString = url.absoluteString
|
|
}
|
|
if let prefillName, name.isEmpty {
|
|
name = prefillName
|
|
}
|
|
if prefillAllowInvalidCertificates {
|
|
allowInvalidCertificates = true
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if os(macOS)
|
|
private var macOSBody: some View {
|
|
Form {
|
|
Section {
|
|
LabeledContent(String(localized: "sources.field.name")) {
|
|
TextField("", text: $name)
|
|
}
|
|
} footer: {
|
|
Text(String(localized: "sources.footer.displayName"))
|
|
.font(.callout)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
Section {
|
|
LabeledContent(String(localized: "sources.placeholder.webdavUrl")) {
|
|
TextField("", text: $urlString)
|
|
.textContentType(.URL)
|
|
.autocorrectionDisabled()
|
|
}
|
|
} footer: {
|
|
Text(String(localized: "sources.footer.webdav"))
|
|
.font(.callout)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
Section {
|
|
LabeledContent(String(localized: "sources.field.usernameOptional")) {
|
|
TextField("", text: $username)
|
|
.textContentType(.username)
|
|
.autocorrectionDisabled()
|
|
}
|
|
LabeledContent(String(localized: "sources.field.passwordOptional")) {
|
|
SecureField("", text: $password)
|
|
.textContentType(.password)
|
|
}
|
|
} header: {
|
|
Text(String(localized: "sources.header.auth"))
|
|
} footer: {
|
|
Text(String(localized: "sources.footer.auth"))
|
|
.font(.callout)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
Section {
|
|
Toggle(String(localized: "sources.field.allowInvalidCertificates"), isOn: $allowInvalidCertificates)
|
|
} header: {
|
|
Text(String(localized: "sources.header.security"))
|
|
} footer: {
|
|
Text(String(localized: "sources.footer.allowInvalidCertificates"))
|
|
.font(.callout)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
if let result = testResult {
|
|
SourceTestResultSection(result: result)
|
|
}
|
|
}
|
|
.formStyle(.grouped)
|
|
.navigationTitle(String(localized: "sources.addWebDAV"))
|
|
.toolbar {
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
Button {
|
|
addSource()
|
|
} label: {
|
|
if isTesting {
|
|
HStack(spacing: 6) {
|
|
ProgressView().controlSize(.small)
|
|
Text(testProgress ?? String(localized: "sources.testing"))
|
|
}
|
|
} else {
|
|
Text(String(localized: "sources.addSource"))
|
|
}
|
|
}
|
|
.disabled(!canAdd || isTesting)
|
|
.keyboardShortcut(.defaultAction)
|
|
}
|
|
}
|
|
.onAppear {
|
|
if let url = prefillURL {
|
|
urlString = url.absoluteString
|
|
}
|
|
if let prefillName, name.isEmpty {
|
|
name = prefillName
|
|
}
|
|
if prefillAllowInvalidCertificates {
|
|
allowInvalidCertificates = true
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// 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)
|
|
}
|
|
}
|