Redline: OneDrive folder transport as alternative to AirDrop (MMDB-2631) #23
@@ -82,21 +82,20 @@ struct SettingsView: View {
|
|||||||
model.chooseOneDriveFolder()
|
model.chooseOneDriveFolder()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HStack(spacing: 8) {
|
// One-line row, same shape as the normal path row: "Not found"
|
||||||
Text("No OneDrive folder found — sign in to OneDrive or choose a folder.")
|
// where the path would be, Choose… stays live. The explanation
|
||||||
.font(.caption)
|
// moves to the caption below instead of wrapping this row.
|
||||||
.foregroundStyle(.secondary)
|
folderValue(path: "Not found") {
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
model.chooseOneDriveFolder()
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
Button("Choose…") { model.chooseOneDriveFolder() }
|
|
||||||
}
|
}
|
||||||
.frame(minHeight: 22)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridRow {
|
GridRow {
|
||||||
Text(
|
Text(
|
||||||
"The PDF is saved here and this same folder is watched for the marked-up copy. On the iPad open it from Files > OneDrive."
|
model.resolvedOneDriveFolder != nil
|
||||||
|
? "The PDF is saved here and this same folder is watched for the marked-up copy. On the iPad open it from Files > OneDrive."
|
||||||
|
: "No OneDrive folder found. Sign in to OneDrive, or choose a folder."
|
||||||
)
|
)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
@@ -249,7 +248,18 @@ extension AppModel: SettingsWindowPresenting {
|
|||||||
/// doesn't exist yet, and re-points the running watcher (folder + recordUncommented)
|
/// doesn't exist yet, and re-points the running watcher (folder + recordUncommented)
|
||||||
/// at the new state. Switching back to AirDrop restores its own stored overrides
|
/// at the new state. Switching back to AirDrop restores its own stored overrides
|
||||||
/// untouched, since AirDrop and OneDrive folder settings are stored under separate keys.
|
/// untouched, since AirDrop and OneDrive folder settings are stored under separate keys.
|
||||||
|
/// Refuses while a send is in flight (send() snapshots its own folder/transport, but
|
||||||
|
/// switching mid-send is still confusing UX — nothing to gain by allowing it).
|
||||||
|
/// The async reconcile below is generation-guarded: `reconcileGeneration` is bumped
|
||||||
|
/// synchronously before the Task starts, and the Task checks its own snapshot against
|
||||||
|
/// the live value before every mutating step, so rapid toggling (this function or
|
||||||
|
/// chooseOneDriveFolder, in any order) always lets the LAST choice win instead of an
|
||||||
|
/// earlier, superseded call applying its stale folder/flag after a later one already won.
|
||||||
func chooseTransport(_ value: SendTransport) {
|
func chooseTransport(_ value: SendTransport) {
|
||||||
|
guard !isSending else {
|
||||||
|
setStatus("Finish the current send first.")
|
||||||
|
return
|
||||||
|
}
|
||||||
guard value != transport else { return }
|
guard value != transport else { return }
|
||||||
TransportSettings.setTransport(value)
|
TransportSettings.setTransport(value)
|
||||||
setTransport(value)
|
setTransport(value)
|
||||||
@@ -261,11 +271,17 @@ extension AppModel: SettingsWindowPresenting {
|
|||||||
}
|
}
|
||||||
setFolderURLs(outbox: folders.outbox, watch: folders.watch)
|
setFolderURLs(outbox: folders.outbox, watch: folders.watch)
|
||||||
setResolvedOneDriveFolder(OneDriveLocator.resolveOneDriveFolder())
|
setResolvedOneDriveFolder(OneDriveLocator.resolveOneDriveFolder())
|
||||||
|
|
||||||
|
reconcileGeneration += 1
|
||||||
|
let generation = reconcileGeneration
|
||||||
Task {
|
Task {
|
||||||
|
guard generation == self.reconcileGeneration else { return }
|
||||||
await watcher.setRecordUncommented(value == .airDrop)
|
await watcher.setRecordUncommented(value == .airDrop)
|
||||||
|
guard generation == self.reconcileGeneration else { return }
|
||||||
do {
|
do {
|
||||||
try await watcher.updateWatchFolder(folders.watch)
|
try await watcher.updateWatchFolder(folders.watch)
|
||||||
} catch {
|
} catch {
|
||||||
|
guard generation == self.reconcileGeneration else { return }
|
||||||
setStatus(
|
setStatus(
|
||||||
(error as? ShotdeckError)?.errorDescription ?? "Could not switch the watch folder."
|
(error as? ShotdeckError)?.errorDescription ?? "Could not switch the watch folder."
|
||||||
)
|
)
|
||||||
@@ -273,7 +289,13 @@ extension AppModel: SettingsWindowPresenting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Refuses while a send is in flight, same reasoning as chooseTransport. See
|
||||||
|
/// chooseTransport's doc comment for the generation-guard mechanism shared here.
|
||||||
func chooseOneDriveFolder() {
|
func chooseOneDriveFolder() {
|
||||||
|
guard !isSending else {
|
||||||
|
setStatus("Finish the current send first.")
|
||||||
|
return
|
||||||
|
}
|
||||||
let start = resolvedOneDriveFolder ?? FileManager.default.homeDirectoryForCurrentUser
|
let start = resolvedOneDriveFolder ?? FileManager.default.homeDirectoryForCurrentUser
|
||||||
guard let url = chooseDirectory(startingAt: start) else { return }
|
guard let url = chooseDirectory(startingAt: start) else { return }
|
||||||
TransportSettings.setOneDriveFolder(url)
|
TransportSettings.setOneDriveFolder(url)
|
||||||
@@ -281,11 +303,17 @@ extension AppModel: SettingsWindowPresenting {
|
|||||||
setResolvedOneDriveFolder(OneDriveLocator.resolveOneDriveFolder())
|
setResolvedOneDriveFolder(OneDriveLocator.resolveOneDriveFolder())
|
||||||
guard transport == .oneDrive else { return }
|
guard transport == .oneDrive else { return }
|
||||||
setFolderURLs(outbox: url, watch: url)
|
setFolderURLs(outbox: url, watch: url)
|
||||||
|
|
||||||
|
reconcileGeneration += 1
|
||||||
|
let generation = reconcileGeneration
|
||||||
Task {
|
Task {
|
||||||
|
guard generation == self.reconcileGeneration else { return }
|
||||||
do {
|
do {
|
||||||
try await watcher.updateWatchFolder(url)
|
try await watcher.updateWatchFolder(url)
|
||||||
|
guard generation == self.reconcileGeneration else { return }
|
||||||
setStatus("OneDrive folder set to \(url.lastPathComponent).")
|
setStatus("OneDrive folder set to \(url.lastPathComponent).")
|
||||||
} catch {
|
} catch {
|
||||||
|
guard generation == self.reconcileGeneration else { return }
|
||||||
setStatus(
|
setStatus(
|
||||||
(error as? ShotdeckError)?.errorDescription ?? "Could not switch the watch folder."
|
(error as? ShotdeckError)?.errorDescription ?? "Could not switch the watch folder."
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user