resolver picker
This commit is contained in:
@@ -6,6 +6,7 @@ import { useWallet, useKitTransactionSigner } from '@solana/connector/react'
|
||||
import { ListingDetail } from '@/components/ListingDetail'
|
||||
import { useTx } from '@/hooks/useTx'
|
||||
import { useListing } from '@/hooks/useListing'
|
||||
import { useResolvers } from '@/hooks/useResolvers'
|
||||
import {
|
||||
getCloseListingInstruction,
|
||||
getCreateOrderInstructionAsync,
|
||||
@@ -13,6 +14,8 @@ import {
|
||||
deriveEscrowId,
|
||||
} from '@solisting/sdk'
|
||||
import { DESCRO_PROGRAM_ADDRESS } from '@descro/sdk'
|
||||
import { ResolverType } from '@descro/sdk'
|
||||
import { Button } from '@/components/ui/Button'
|
||||
import type { Address } from '@solisting/sdk'
|
||||
|
||||
const SYSTEM_PROGRAM = '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>
|
||||
@@ -24,7 +27,10 @@ export default function ListingPage({ params }: { params: Promise<{ pk: string }
|
||||
const { signer } = useKitTransactionSigner()
|
||||
const sendTx = useTx()
|
||||
const { data: listing } = useListing(pk as Address)
|
||||
const { data: resolvers = [] } = useResolvers()
|
||||
const [orderLoading, setOrderLoading] = useState(false)
|
||||
const [showResolverPicker, setShowResolverPicker] = useState(false)
|
||||
const [selectedResolver, setSelectedResolver] = useState<Address | null>(null)
|
||||
|
||||
async function handleClose() {
|
||||
if (!signer) return
|
||||
@@ -33,8 +39,14 @@ export default function ListingPage({ params }: { params: Promise<{ pk: string }
|
||||
router.push('/listings')
|
||||
}
|
||||
|
||||
async function handlePlaceOrder() {
|
||||
function openPlaceOrder() {
|
||||
setSelectedResolver(null)
|
||||
setShowResolverPicker(true)
|
||||
}
|
||||
|
||||
async function handleConfirmOrder() {
|
||||
if (!signer || !account || !listing) return
|
||||
setShowResolverPicker(false)
|
||||
setOrderLoading(true)
|
||||
try {
|
||||
const orderId = BigInt(Date.now())
|
||||
@@ -50,7 +62,7 @@ export default function ListingPage({ params }: { params: Promise<{ pk: string }
|
||||
descroProgram: DESCRO_PROGRAM_ADDRESS,
|
||||
orderId,
|
||||
escrowId,
|
||||
resolver: account as Address,
|
||||
resolver: selectedResolver,
|
||||
paymentCurrency: { __kind: 'Sol' },
|
||||
expectedAmount: listing.data.price,
|
||||
maxSlippageBps: 0,
|
||||
@@ -67,13 +79,101 @@ export default function ListingPage({ params }: { params: Promise<{ pk: string }
|
||||
}
|
||||
}
|
||||
|
||||
const acceptedResolvers = listing?.data.acceptedResolvers ?? []
|
||||
const visibleResolvers = acceptedResolvers.length > 0
|
||||
? resolvers.filter((r) => acceptedResolvers.includes(r.pda as Address))
|
||||
: resolvers
|
||||
const mustSelectResolver = acceptedResolvers.length > 0
|
||||
const confirmDisabled = mustSelectResolver && !selectedResolver
|
||||
|
||||
return (
|
||||
<ListingDetail
|
||||
pk={pk as Address}
|
||||
walletAddress={account ?? null}
|
||||
onUpdate={() => router.push(`/listing/create?edit=${pk}`)}
|
||||
onClose={handleClose}
|
||||
onPlaceOrder={orderLoading ? undefined : handlePlaceOrder}
|
||||
/>
|
||||
<>
|
||||
<ListingDetail
|
||||
pk={pk as Address}
|
||||
walletAddress={account ?? null}
|
||||
onUpdate={() => router.push(`/listing/create?edit=${pk}`)}
|
||||
onClose={handleClose}
|
||||
onPlaceOrder={orderLoading ? undefined : openPlaceOrder}
|
||||
/>
|
||||
|
||||
{showResolverPicker && (
|
||||
<div
|
||||
style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.55)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 100 }}
|
||||
onClick={(e) => { if (e.target === e.currentTarget) setShowResolverPicker(false) }}
|
||||
>
|
||||
<div style={{ background: 'var(--bg)', border: '1px solid var(--bd)', borderRadius: 'var(--radius)', padding: '28px 28px 24px', width: 440, maxWidth: '90vw', maxHeight: '80vh', overflow: 'auto' }}>
|
||||
<div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '.14em', color: 'var(--mut)', marginBottom: 12 }}>PLACE ORDER</div>
|
||||
<h2 style={{ margin: '0 0 8px', fontSize: 19, fontWeight: 700 }}>Select Resolver</h2>
|
||||
<p style={{ margin: '0 0 20px', fontSize: 13, color: 'var(--mut)', lineHeight: 1.55 }}>
|
||||
{mustSelectResolver
|
||||
? 'This listing requires one of the following resolvers for dispute resolution.'
|
||||
: 'Choose a resolver for dispute resolution, or proceed without one.'}
|
||||
</p>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 22 }}>
|
||||
{!mustSelectResolver && (
|
||||
<ResolverOption
|
||||
selected={selectedResolver === null}
|
||||
onClick={() => setSelectedResolver(null)}
|
||||
label="No resolver"
|
||||
sublabel="Disputes cannot be resolved on-chain"
|
||||
/>
|
||||
)}
|
||||
{visibleResolvers.map((r) => (
|
||||
<ResolverOption
|
||||
key={r.pda}
|
||||
selected={selectedResolver === r.pda}
|
||||
onClick={() => setSelectedResolver(r.pda as Address)}
|
||||
label={`${r.pda.slice(0, 8)}…${r.pda.slice(-6)}`}
|
||||
sublabel={ResolverType[r.data.resolverType as number] as string}
|
||||
mono
|
||||
/>
|
||||
))}
|
||||
{visibleResolvers.length === 0 && mustSelectResolver && (
|
||||
<div style={{ fontSize: 13, color: 'var(--mut)', padding: '12px 0' }}>
|
||||
No registered resolvers found for this listing.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
|
||||
<Button variant="ghost" onClick={() => setShowResolverPicker(false)}>Cancel</Button>
|
||||
<Button onClick={handleConfirmOrder} disabled={confirmDisabled}>Place Order</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function ResolverOption({ selected, onClick, label, sublabel, mono }: {
|
||||
selected: boolean
|
||||
onClick: () => void
|
||||
label: string
|
||||
sublabel?: string
|
||||
mono?: boolean
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
|
||||
padding: '11px 14px', borderRadius: 9, cursor: 'pointer', textAlign: 'left',
|
||||
background: selected ? 'var(--accSoft)' : 'var(--bg3)',
|
||||
border: `1px solid ${selected ? 'var(--accBd)' : 'var(--bd)'}`,
|
||||
color: 'var(--tx)',
|
||||
transition: 'background .12s, border-color .12s',
|
||||
}}
|
||||
>
|
||||
<span style={{ fontFamily: mono ? 'var(--font-mono)' : undefined, fontSize: mono ? 12.5 : 13, fontWeight: selected ? 600 : mono ? 400 : 600 }}>
|
||||
{label}
|
||||
</span>
|
||||
{sublabel && (
|
||||
<span style={{ fontSize: 11.5, color: 'var(--mut)', flexShrink: 0 }}>
|
||||
{sublabel}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user