Last fixes

- Pending Delegate events with registry-miss identity keep explorer navigation; unbonded label limited to pending Undelegate rows (`isPendingUndelegateWithRegistryMiss`, `formatPendingDelegationLinkLabel`).
- Tests in `delegationIdentity.test.ts`.
This commit is contained in:
Tommy Verrall
2026-06-08 12:38:09 +02:00
parent c52fc0c9af
commit 2c1b5f59a3
5 changed files with 61 additions and 27 deletions
@@ -319,28 +319,9 @@ export const DelegationList: FCWithChildren<{
Pending Pending
</Typography> </Typography>
<Stack spacing={2}> <Stack spacing={2}>
{pendingItems.map((item: any, index: number) => { {pendingItems.map((item: any, index: number) => (
if ( <PendingDelegationCard key={pendingKey(item, `p-${index}`)} item={item} explorerUrl={explorerUrl} />
item.event && ))}
item.event.kind === 'Delegate' &&
(!item.node_identity || item.node_identity === '')
) {
return (
<PendingDelegationCard
key={pendingKey(item, `d-${index}`)}
item={{
...item,
node_identity: `Mix Identity Key ${item.event.mix_id}`,
}}
explorerUrl={explorerUrl}
/>
);
}
return (
<PendingDelegationCard key={pendingKey(item, `p-${index}`)} item={item} explorerUrl={explorerUrl} />
);
})}
</Stack> </Stack>
</Stack> </Stack>
)} )}
@@ -2,11 +2,16 @@ import React from 'react';
import { Box, Chip, Paper, Stack, Tooltip, Typography } from '@mui/material'; import { Box, Chip, Paper, Stack, Tooltip, Typography } from '@mui/material';
import { WrappedDelegationEvent } from '@nymproject/types'; import { WrappedDelegationEvent } from '@nymproject/types';
import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { TauriLink as Link } from 'src/components/TauriLinkWrapper';
import { formatDelegationNodeIdentityForDisplay, isUnbondedNodeIdentity } from 'src/utils/delegationIdentity'; import {
formatDelegationNodeIdentityForDisplay,
formatPendingDelegationLinkLabel,
isPendingUndelegateWithRegistryMiss,
} from 'src/utils/delegationIdentity';
export const PendingDelegationCard = ({ item, explorerUrl }: { item: WrappedDelegationEvent; explorerUrl: string }) => { export const PendingDelegationCard = ({ item, explorerUrl }: { item: WrappedDelegationEvent; explorerUrl: string }) => {
const pendingUndelegateRegistryMiss = isPendingUndelegateWithRegistryMiss(item);
const displayIdentity = formatDelegationNodeIdentityForDisplay(item.node_identity, item.event.mix_id); const displayIdentity = formatDelegationNodeIdentityForDisplay(item.node_identity, item.event.mix_id);
const nodeIsUnbonded = isUnbondedNodeIdentity(item.node_identity); const linkLabel = formatPendingDelegationLinkLabel(item.node_identity, item.event.mix_id);
return ( return (
<Paper <Paper
@@ -20,7 +25,7 @@ export const PendingDelegationCard = ({ item, explorerUrl }: { item: WrappedDele
}} }}
> >
<Stack spacing={1.5} direction={{ xs: 'column', sm: 'row' }} alignItems={{ sm: 'center' }} flexWrap="wrap"> <Stack spacing={1.5} direction={{ xs: 'column', sm: 'row' }} alignItems={{ sm: 'center' }} flexWrap="wrap">
{nodeIsUnbonded ? ( {pendingUndelegateRegistryMiss ? (
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
{displayIdentity} {displayIdentity}
</Typography> </Typography>
@@ -28,7 +33,7 @@ export const PendingDelegationCard = ({ item, explorerUrl }: { item: WrappedDele
<Link <Link
target="_blank" target="_blank"
href={`${explorerUrl}/nodes/${item.event.mix_id}`} href={`${explorerUrl}/nodes/${item.event.mix_id}`}
text={`${item.node_identity.slice(0, 6)}...${item.node_identity.slice(-6)}`} text={linkLabel}
color="text.primary" color="text.primary"
noIcon noIcon
/> />
@@ -2,9 +2,12 @@ import {
UNBONDED_NODE_IDENTITY_PREFIX, UNBONDED_NODE_IDENTITY_PREFIX,
formatUnbondedNodeLabel, formatUnbondedNodeLabel,
formatDelegationNodeIdentityForDisplay, formatDelegationNodeIdentityForDisplay,
formatPendingDelegationLinkLabel,
isFullyUnbondedDelegation, isFullyUnbondedDelegation,
isPendingUndelegateWithRegistryMiss,
isUnbondedNodeIdentity, isUnbondedNodeIdentity,
} from './delegationIdentity'; } from './delegationIdentity';
import { buildPendingDelegateEvent, buildPendingUndelegateEvent } from './unbondedDelegation.fixture';
describe('delegationIdentity', () => { describe('delegationIdentity', () => {
it('treats empty identity as unbonded', () => { it('treats empty identity as unbonded', () => {
@@ -35,4 +38,20 @@ describe('delegationIdentity', () => {
'2Abcdefghijklmnopqrstuvwxyz1234567890', '2Abcdefghijklmnopqrstuvwxyz1234567890',
); );
}); });
it('uses unbonded label only for pending undelegate registry misses', () => {
const pendingDelegate = buildPendingDelegateEvent(`unbonded:${42}`);
const pendingUndelegate = buildPendingUndelegateEvent(`unbonded:${42}`);
expect(isPendingUndelegateWithRegistryMiss(pendingDelegate)).toBe(false);
expect(isPendingUndelegateWithRegistryMiss(pendingUndelegate)).toBe(true);
});
it('formats pending delegate explorer link label by mix id when identity lookup missed', () => {
expect(formatPendingDelegationLinkLabel('', 788)).toBe('Mix 788');
expect(formatPendingDelegationLinkLabel('unbonded:788', 788)).toBe('Mix 788');
expect(formatPendingDelegationLinkLabel('2Abcdefghijklmnopqrstuvwxyz1234567890', 788)).toBe(
'2Abcde...567890',
);
});
}); });
+14 -1
View File
@@ -1,4 +1,4 @@
import type { DelegationWithEverything } from '@nymproject/types'; import type { DelegationWithEverything, WrappedDelegationEvent } from '@nymproject/types';
export const UNBONDED_NODE_IDENTITY_PREFIX = 'unbonded:'; export const UNBONDED_NODE_IDENTITY_PREFIX = 'unbonded:';
@@ -25,3 +25,16 @@ export function formatDelegationNodeIdentityForDisplay(nodeIdentity: string, mix
} }
return nodeIdentity; return nodeIdentity;
} }
export function isPendingUndelegateWithRegistryMiss(
item: Pick<WrappedDelegationEvent, 'node_identity' | 'event'>,
): boolean {
return item.event.kind === 'Undelegate' && isUnbondedNodeIdentity(item.node_identity);
}
export function formatPendingDelegationLinkLabel(nodeIdentity: string, mixId: number): string {
if (!nodeIdentity || isUnbondedNodeIdentity(nodeIdentity)) {
return `Mix ${mixId}`;
}
return `${nodeIdentity.slice(0, 6)}...${nodeIdentity.slice(-6)}`;
}
@@ -69,3 +69,19 @@ export function buildPendingUndelegateEvent(
}, },
}; };
} }
export function buildPendingDelegateEvent(
nodeIdentity: string,
mixId: number = EXAMPLE_UNBONDED_MIX_ID,
): WrappedDelegationEvent {
return {
node_identity: nodeIdentity,
event: {
kind: 'Delegate',
mix_id: mixId,
address: EXAMPLE_DELEGATOR_ADDRESS,
amount: { amount: '1000000', denom: 'nym' },
proxy: null,
},
};
}