diff --git a/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentWithPriceView.swift b/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentWithPriceView.swift index 850adabc68..3c71427663 100644 --- a/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentWithPriceView.swift +++ b/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentWithPriceView.swift @@ -117,6 +117,21 @@ struct DocumentWithPriceView: View { .foregroundColor(.secondary) } } + .onAppear { + // `onChange(of: documentId)` does NOT fire when the id is + // established as the binding mounts — e.g. PurchaseDocumentView + // seeds `documentIdField` in its own `onAppear` and disables this + // view, so relying on onChange alone the price probe would never + // run and Purchase would stay gated off forever. Kick the fetch + // here so a pre-seeded id loads its price without a user edit. + // `.disabled(true)` only blocks hit-testing, not lifecycle events, + // so this still fires in the Purchase flow. When the field starts + // empty (the editable transition flow) this is a no-op and the + // existing onChange path continues to handle typing. + if !documentId.isEmpty { + handleDocumentIdChange(documentId) + } + } } private func handleDocumentIdChange(_ newValue: String) { diff --git a/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentsView.swift b/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentsView.swift index 7afcbc6b43..dd4ddff535 100644 --- a/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentsView.swift +++ b/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentsView.swift @@ -275,6 +275,10 @@ struct DocumentDetailView: View { private var availableActions: [DocumentAction] { var actions: [DocumentAction] = [] let docType = documentTypeRow + // `tradeMode == 1` is DirectPurchase — the only mode that supports + // listing/buying — matching the marketplace gating in + // TransitionInputView (`$0.tradeMode == 1`). + let tradeable = (docType?.tradeMode ?? 0) == 1 if ownerIsControlled { actions.append(.replace) @@ -282,12 +286,18 @@ struct DocumentDetailView: View { if docType?.documentsTransferable == true { actions.append(.transfer) } - if (docType?.tradeMode ?? 0) > 0 { + if tradeable { actions.append(.setPrice) } - } else if (docType?.tradeMode ?? 0) > 0 && !nonOwnerControlledIdentities.isEmpty { - // Not the owner, but the wallet holds another identity that - // could buy a for-sale, tradeable document. + } + // Surface Purchase whenever the doc type is tradeable and the wallet + // holds a controlled identity that isn't the owner (the buyer ≠ + // owner, and the buyer signs). This intentionally also covers a doc + // owned by another *controlled* identity — the two-identities-in-one + // -app flow — not just externally-owned docs, matching the doc + // comment above. The Purchase sheet resolves the real on-chain price + // / for-sale state and disables the button when it isn't for sale. + if tradeable && !nonOwnerControlledIdentities.isEmpty { actions.append(.purchase) } return actions