The board chooses where it lives — swipe-open settings move it between iCloud and this iPhone
A trailing swipe on a board row opens Board Settings, whose first setting is location: iCloud or Local, with a confirmed move to the other side — destructive-styled only outbound, because leaving iCloud is the direction that sheds protection. The move is setUbiquitous against the real container and a coordinated move under the DEBUG stand-in; evacuation sweeps materialization first and refuses honestly while content is still downloading. The local home is the sandbox Documents folder, published to the Files app, so a local board is still a folder the user owns. With a second home the iCloud wall softens (user-ruled 2026-08-08): the index always reaches ready, cloud unavailability becomes an inline notice with a retry, creates land locally when there is no account, and LANEWORK_FORCE_NO_ICLOUD makes that state reproducible in tests regardless of the machine's sign-in. Known gap, now user-reachable: backup remains iCloud-only, so local boards sit outside it. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -30,6 +30,32 @@ struct BoardSummary: Identifiable, Sendable, Equatable {
|
||||
let modified: Date?
|
||||
|
||||
let download: BoardDownloadState
|
||||
|
||||
/// Which of the phone's two homes this board is in.
|
||||
let location: BoardLocation
|
||||
}
|
||||
|
||||
/// Where a board is stored, and therefore whether it syncs.
|
||||
///
|
||||
/// **Told, never sniffed.** A location is what the home an entry was enumerated from *is*, so the
|
||||
/// index tags it at the point where that is a fact (`BoardIndexStore`, which holds both roots) and
|
||||
/// everything downstream carries the tag. Deriving it from the URL instead would mean deciding
|
||||
/// whether a path is inside a ubiquity container by looking at it — a question that has no stable
|
||||
/// answer across the real container, the DEBUG stand-in root and a test's scratch directories.
|
||||
enum BoardLocation: Sendable, Equatable {
|
||||
/// The `CloudHome` — syncs to the Mac and to every other device on the account.
|
||||
case icloud
|
||||
|
||||
/// The `DeviceHomeResolver` home — this phone, and nowhere else.
|
||||
case local
|
||||
|
||||
/// The one word the list marker, the settings sheet and its move button all name it by.
|
||||
var name: String {
|
||||
switch self {
|
||||
case .icloud: "iCloud"
|
||||
case .local: "Local"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a board's bytes are on this device — the summary-level reading, from the metadata item's
|
||||
@@ -69,6 +95,9 @@ struct BoardIndexEntry: Sendable, Equatable {
|
||||
let rootURL: URL
|
||||
let modified: Date?
|
||||
let download: BoardDownloadState
|
||||
|
||||
/// Set by whoever produced the entry, from the home it enumerated — see `BoardLocation`.
|
||||
let location: BoardLocation
|
||||
}
|
||||
|
||||
/// Turns an index entry into a summary by looking, shallowly, at the package.
|
||||
@@ -106,7 +135,8 @@ enum BoardSummaryScanner {
|
||||
laneCount: nil,
|
||||
cardCount: nil,
|
||||
modified: entry.modified,
|
||||
download: entry.download
|
||||
download: entry.download,
|
||||
location: entry.location
|
||||
)
|
||||
}
|
||||
|
||||
@@ -123,7 +153,8 @@ enum BoardSummaryScanner {
|
||||
laneCount: nil,
|
||||
cardCount: nil,
|
||||
modified: entry.modified,
|
||||
download: entry.download
|
||||
download: entry.download,
|
||||
location: entry.location
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,7 +171,8 @@ enum BoardSummaryScanner {
|
||||
laneCount: lanes,
|
||||
cardCount: cards,
|
||||
modified: entry.modified,
|
||||
download: entry.download
|
||||
download: entry.download,
|
||||
location: entry.location
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user