Major update for stability
This commit is contained in:
@@ -38,6 +38,8 @@ struct JobEditorView: View {
|
||||
@State private var watchEnabled: Bool
|
||||
@State private var watchPath: String
|
||||
@State private var watchDebounceSeconds: Int
|
||||
@State private var allowUnattendedDelete: Bool
|
||||
@State private var validationMessage: String?
|
||||
|
||||
init(job: SyncJob? = nil, initialSection: JobEditorSection? = nil) {
|
||||
self.existingJob = job
|
||||
@@ -68,6 +70,8 @@ struct JobEditorView: View {
|
||||
_watchPath = State(initialValue: job?.watchPath ?? "")
|
||||
let defaultDebounce = UserDefaults.standard.integer(forKey: "defaultWatchDebounceSeconds")
|
||||
_watchDebounceSeconds = State(initialValue: job?.watchDebounceSeconds ?? (defaultDebounce == 0 ? 3 : defaultDebounce))
|
||||
_allowUnattendedDelete = State(initialValue: job?.allowUnattendedDelete ?? false)
|
||||
_validationMessage = State(initialValue: nil)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -174,6 +178,12 @@ struct JobEditorView: View {
|
||||
Toggle("Archive mode (-a)", isOn: $archive)
|
||||
Toggle("Compress during transfer (-z)", isOn: $compress)
|
||||
Toggle("Delete files missing from source (--delete)", isOn: $deleteExtraneous)
|
||||
if deleteExtraneous && (scheduleEnabled || watchEnabled) {
|
||||
Toggle("Allow unattended delete", isOn: $allowUnattendedDelete)
|
||||
Text("Required before scheduled or file-watch runs may use --delete. Enable only after validating the job with Dry Run.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.orange)
|
||||
}
|
||||
Toggle("Preserve permissions", isOn: $preservePermissions)
|
||||
Toggle("Preserve extended attributes", isOn: $preserveExtendedAttributes)
|
||||
Toggle("Show transfer progress", isOn: $showProgress)
|
||||
@@ -191,6 +201,9 @@ struct JobEditorView: View {
|
||||
Section("Advanced") {
|
||||
TextField("Additional rsync arguments", text: $additionalArguments)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
Text("Transport, password, remote-command and destructive options are blocked here. Use the dedicated controls above for SSH and --delete.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.formStyle(.grouped)
|
||||
@@ -213,6 +226,14 @@ struct JobEditorView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert("Invalid Sync Job", isPresented: Binding(
|
||||
get: { validationMessage != nil },
|
||||
set: { if !$0 { validationMessage = nil } }
|
||||
)) {
|
||||
Button("OK", role: .cancel) { validationMessage = nil }
|
||||
} message: {
|
||||
Text(validationMessage ?? "The job configuration is invalid.")
|
||||
}
|
||||
.frame(width: 690, height: 820)
|
||||
}
|
||||
|
||||
@@ -262,10 +283,22 @@ struct JobEditorView: View {
|
||||
job.watchEnabled = watchEnabled
|
||||
job.watchPath = watchPath
|
||||
job.watchDebounceSeconds = watchDebounceSeconds
|
||||
job.allowUnattendedDelete = deleteExtraneous ? allowUnattendedDelete : false
|
||||
job.updatedAt = Date()
|
||||
if existingJob == nil { modelContext.insert(job) }
|
||||
try? modelContext.save()
|
||||
NotificationCenter.default.post(name: .syncJobsChanged, object: nil)
|
||||
dismiss()
|
||||
|
||||
let validationTrigger: SyncRunTrigger = (job.scheduleEnabled || job.watchEnabled) ? .scheduled : .manual
|
||||
if let message = RsyncSecurityValidator.validationMessage(for: job, trigger: validationTrigger) {
|
||||
validationMessage = message
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
if existingJob == nil { modelContext.insert(job) }
|
||||
try modelContext.save()
|
||||
NotificationCenter.default.post(name: .syncJobsChanged, object: nil)
|
||||
dismiss()
|
||||
} catch {
|
||||
validationMessage = "Could not save the job: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user