From b53afd89a51dbf1ff38d826946cbdf12cc144984 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 1 Sep 2026 12:57:20 +0200 Subject: [PATCH] fix: show loading and unavailable states for operator balance and fee The operator detail page rendered a confident "0 ETH" for Balance and Annual Fee whenever the on-chain reads were pending or failing, because useOperatorEarningsAndFees coalesced `data ?? 0n`. - expose earningsStatus / feesStatus from the hook via combineQueryStatus - treat fetchStatus "paused" as unavailable: a read that cannot reach the RPC parks at status "pending" and never resolves to an error, which would otherwise leave the UI on a skeleton indefinitely - render a skeleton while pending and an explicit unavailable message instead of a zero amount - gate Withdraw and Update Fee on query success rather than on the coalesced bigint, so a failed read no longer leaves Update Fee clickable into the fee wizard --- .../routes/dashboard/operators/operator.tsx | 117 +++++++++++++----- .../use-operator-earnings-and-fees.ts | 43 ++++++- 2 files changed, 125 insertions(+), 35 deletions(-) diff --git a/src/app/routes/dashboard/operators/operator.tsx b/src/app/routes/dashboard/operators/operator.tsx index 649107d72..5ac865e8e 100644 --- a/src/app/routes/dashboard/operators/operator.tsx +++ b/src/app/routes/dashboard/operators/operator.tsx @@ -19,15 +19,43 @@ import { IncreaseOperatorFeeStatusBadge } from "@/components/operator/increase-o import { OperatorValidatorsList } from "@/components/operator/operator-validators-list"; import { OperatorStatusBadge } from "@/components/operator/operator-status-badge"; import { useOperatorEarningsAndFees } from "@/hooks/operator/use-operator-earnings-and-fees"; +import { Skeleton } from "@/components/ui/skeleton"; + +const AmountSkeleton: FC<{ "data-testid"?: string }> = (props) => ( +
+ + +
+); + +const AmountUnavailable: FC<{ "data-testid"?: string }> = (props) => ( +
+ + Couldn't read this value from the network. + + + It will appear once the connection recovers. + +
+); export const Operator: FC> = ({ ...props }) => { const params = useOperatorPageParams(); const operatorId = BigInt(params.operatorId!); const operator = useOperator(operatorId!); - const { feeEth, yearlyFeeEth, yearlyFeeSSV, balanceEth, balanceSSV } = - useOperatorEarningsAndFees(operatorId); + const { + feeEth, + yearlyFeeEth, + yearlyFeeSSV, + balanceEth, + balanceSSV, + earningsStatus, + feesStatus, + } = useOperatorEarningsAndFees(operatorId); - const hasBalance = balanceEth > 0n || balanceSSV > 0n; + const hasBalance = + earningsStatus.isSuccess && (balanceEth > 0n || balanceSSV > 0n); + const canUpdateFee = feesStatus.isSuccess && feeEth.data !== 0n; if (!operator.data) return null; @@ -111,7 +139,10 @@ export const Operator: FC> = ({ ...props }) => {
- + > = ({ ...props }) => { > Balance -
- - {yearlyFeeSSV !== 0n && !operator.data.migrated && ( + {earningsStatus.isUnavailable ? ( + + ) : earningsStatus.isPending ? ( + + ) : ( +
- )} -
+ {yearlyFeeSSV !== 0n && !operator.data.migrated && ( + + )} +
+ )}
-
- - {yearlyFeeSSV > 0 && ( + {feesStatus.isUnavailable ? ( + + ) : feesStatus.isPending ? ( + + ) : ( +
- )} -
+ {yearlyFeeSSV > 0 && ( + + )} +
+ )} Operators with a fee of 0 cannot change their fee.{" "}