diff --git a/MARKETPLACE_FLOW_AUDIT.md b/MARKETPLACE_FLOW_AUDIT.md index e22040c..0438787 100644 --- a/MARKETPLACE_FLOW_AUDIT.md +++ b/MARKETPLACE_FLOW_AUDIT.md @@ -222,6 +222,11 @@ Second batch (payment, attestation, escrow): escrow (`item.escrow === 'required'`); ask-seller is available on an active listing (not only once sold); payment receipts are scoped to the payer's pubkey, so a stale/foreign receipt cannot be attached by another account. +14. **Outage states (trade/reputation/seller/shipping):** `TradesPage`, + `SellerDashboard`, `TradeReputationPanel`, `TradeReputationCard` and + `ShippingStatusCard` now show an explicit "could not reach the relays" + notice (`marketUi.loadFailed`) instead of an empty / "nothing waiting" / + "no trades" state when their query errors. ## 7. Recommended order diff --git a/src/components/SellerDashboard.tsx b/src/components/SellerDashboard.tsx index de737c7..15c0122 100644 --- a/src/components/SellerDashboard.tsx +++ b/src/components/SellerDashboard.tsx @@ -73,7 +73,7 @@ function MyListingRow({ item }: { item: MarketplaceItem }) { export function SellerDashboard() { const { t } = useTranslation(); const { user } = useCurrentUser(); - const { data: items, isLoading } = useMarketplaceItems(200); + const { data: items, isLoading, isError } = useMarketplaceItems(200); const mine = (items ?? []).filter((i) => i.pubkey === user?.pubkey); const active = mine.filter((i) => i.status !== 'sold'); @@ -98,7 +98,9 @@ export function SellerDashboard() { - {isLoading ? ( + {isError ? ( +
{t('marketUi.loadFailed')}
+ ) : isLoading ? ({t('marketUi.seller.loading')}
) : mine.length === 0 ? ({t('marketUi.loadFailed')}
} + setTracking(e.target.value)} diff --git a/src/components/TradeReputationCard.tsx b/src/components/TradeReputationCard.tsx index 4844382..f819fdf 100644 --- a/src/components/TradeReputationCard.tsx +++ b/src/components/TradeReputationCard.tsx @@ -24,7 +24,7 @@ function Stat({ label, value, accent }: { label: string; value: number; accent?: */ export function TradeReputationCard({ pubkey, className }: { pubkey: string; className?: string }) { const { t } = useTranslation(); - const { data } = useTradeReputation(pubkey); + const { data, isError } = useTradeReputation(pubkey); const verifiedWith = data?.verifiedWith ?? []; const partnerPubkeys = verifiedWith.map((v) => v.pubkey); // Trust-weight each mutually-confirmed trade by the counterparty's web-of-trust @@ -36,6 +36,10 @@ export function TradeReputationCard({ pubkey, className }: { pubkey: string; cla const { data: rings } = useTradeRings(pubkey, partnerPubkeys); const total = (data?.sales ?? 0) + (data?.purchases ?? 0); + // A relay outage must not silently read as "no reputation". + if (isError) { + return{t('marketUi.loadFailed')}
; + } if (!data || total === 0) return null; const people = data.verifiedCounterparties; diff --git a/src/components/TradeReputationPanel.tsx b/src/components/TradeReputationPanel.tsx index f7bb677..b1ce7cf 100644 --- a/src/components/TradeReputationPanel.tsx +++ b/src/components/TradeReputationPanel.tsx @@ -93,7 +93,7 @@ function ReviewSkeleton() { */ export function TradeReputationPanel({ pubkey }: { pubkey: string }) { const { t } = useTranslation(); - const { data, isLoading } = useTradeReputation(pubkey); + const { data, isLoading, isError } = useTradeReputation(pubkey); const attestations = [...(data?.attestations ?? [])].sort((a, b) => b.created_at - a.created_at); const reviews = attestations.filter((a) => a.pubkey !== pubkey); const ratings = attestations @@ -126,7 +126,9 @@ export function TradeReputationPanel({ pubkey }: { pubkey: string }) { )} - {isLoading ? ( + {isError ? ( +{t('marketUi.loadFailed')}
+ ) : isLoading ? ({t('marketUi.loadFailed')}
+ )} + + {!tradeError && !loadingIncoming && !loadingPending && actionNeeded === 0 && ({t('marketUi.trades.nothingWaiting')}
)} @@ -239,7 +245,11 @@ export default function TradesPage() { {loadingRep &&{t('marketUi.loadFailed')}
+ )} + + {!repError && !loadingRep && authored.length === 0 && (