Treat order as presentation in seed logic; add per-seed restore and routine duplication
Drag-to-reorder writes Routine.order through the document, which used to read as a content edit: isPristine would fork a starter for a mere reorder, and reconcile's semantic compare would clobber a reordered seed file back to bundle order. Both now normalize order (and updatedAt) away — a fixed- ULID file still never holds user content; ordering is bookkeeping. SyncEngine gains restoreSeed(id:) — the per-seed analog of the bulk restore, sharing one restoreSeedIfEligible core — and duplicate(routine:), which copies any routine (starter or not) to a fresh ULID with fresh exercise ids, a unique "… Copy" name, and last position in the list. Claude-Session: https://claude.ai/code/session_01H8VxUX4ckjU3vRF5M4L5FV
This commit is contained in:
@@ -52,18 +52,23 @@ enum SeedLibrary {
|
||||
|
||||
static func seed(id: String) -> Seed? { seeds.first { $0.id == id } }
|
||||
|
||||
/// True when `doc` is unchanged from its pristine seed in every field except
|
||||
/// `updatedAt`. The edit sheets stamp `updatedAt` on every Save, so a no-op save
|
||||
/// would otherwise read as an edit and fork the seed; normalizing both timestamps
|
||||
/// to a common value before the `==` isolates real changes. A non-seed id has no
|
||||
/// True when `doc` is unchanged from its pristine seed in every field that counts
|
||||
/// as *content* — i.e. ignoring `updatedAt` and `order`, both of which are
|
||||
/// presentation/bookkeeping rather than content. The edit sheets stamp `updatedAt`
|
||||
/// on every Save, so a no-op save would otherwise read as an edit and fork the seed;
|
||||
/// and dragging a starter to reorder the list writes a new `order` to it — a reorder
|
||||
/// must NOT fork a curated starter into a user routine. Normalizing both fields to a
|
||||
/// common value before the `==` isolates real content changes. A non-seed id has no
|
||||
/// seed to compare against — treated as pristine (callers gate on `isSeed` first).
|
||||
static func isPristine(_ doc: RoutineDocument) -> Bool {
|
||||
guard let seed = seed(id: doc.id) else { return true }
|
||||
var edited = doc
|
||||
var pristine = seed.doc
|
||||
let common = Date(timeIntervalSince1970: 0)
|
||||
edited.updatedAt = common
|
||||
pristine.updatedAt = common
|
||||
let commonDate = Date(timeIntervalSince1970: 0)
|
||||
edited.updatedAt = commonDate
|
||||
pristine.updatedAt = commonDate
|
||||
edited.order = 0
|
||||
pristine.order = 0
|
||||
return edited == pristine
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user