Merge branch 'cursor/8e13a949'

Inline crypto address validation on check instead of window.alert.
This commit is contained in:
Tommaso Casaburi
2026-05-19 15:05:47 +07:00
2 changed files with 45 additions and 3 deletions
@@ -28,7 +28,7 @@ vi.mock('react-i18next', () => ({
crypto_address_not_yours: 'Crypto address is not yours.',
crypto_address_verification: 'if the crypto address is resolved p2p',
crypto_address_yours: 'Crypto address belongs to this account.',
enter_crypto_address: 'Enter crypto address.',
enter_crypto_address: 'Please enter a valid crypto address.',
loading: 'loading',
save: 'save',
saved: 'saved',
@@ -152,6 +152,34 @@ describe('CryptoAddressSetting', () => {
expect(getInput().value).toBe('resolved-alias.eth');
});
it('shows a transient validation message when check is clicked with an empty field', async () => {
hookMocks.useAccount.mockReturnValue({
author: {
address: '12D3KooWSignerPublicKey',
shortAddress: '12D3KooWSignerPublicKey',
},
signer: {
address: SIGNER_ADDRESS,
},
});
await render();
await act(async () => {
getButtonByText('check').click();
});
expect(container.textContent).toContain('Please enter a valid crypto address.');
expect(alertSpy).not.toHaveBeenCalled();
await act(async () => {
vi.advanceTimersByTime(2000);
});
expect(container.textContent).toContain('if the crypto address is resolved p2p');
expect(container.textContent).not.toContain('Please enter a valid crypto address.');
});
it('updates the displayed status after async resolution completes', async () => {
await render();
@@ -85,12 +85,22 @@ const showSavedIndicator = (setSavedCryptoAddress: (value: boolean) => void) =>
}, 2000);
};
type ResolutionStatus = ReturnType<typeof getResolutionStatus>;
const showTransientResolutionStatus = (setTransientResolutionStatus: (value: ResolutionStatus | undefined) => void, status: ResolutionStatus) => {
setTransientResolutionStatus(status);
setTimeout(() => {
setTransientResolutionStatus(undefined);
}, 2000);
};
const CryptoAddressSettingContent = ({ account }: { account: ReturnType<typeof useAccount> }) => {
const { t } = useTranslation();
const [cryptoAddress, setCryptoAddress] = useState(() => getInitialCryptoAddress(account?.author?.address));
const [checkedAddress, setCheckedAddress] = useState<string>();
const [savedCryptoAddress, setSavedCryptoAddress] = useState(false);
const [showCryptoAddressInfo, setShowCryptoAddressInfo] = useState(false);
const [transientResolutionStatus, setTransientResolutionStatus] = useState<ResolutionStatus>();
const signerAddress = account?.signer?.address;
const authorToResolve = checkedAddress ? { ...account?.author, address: checkedAddress } : undefined;
@@ -108,7 +118,10 @@ const CryptoAddressSettingContent = ({ account }: { account: ReturnType<typeof u
const checkCryptoAddress = () => {
const addressToCheck = cryptoAddress.trim();
if (!addressToCheck || !addressToCheck.includes('.')) {
alert(t('enter_crypto_address'));
showTransientResolutionStatus(setTransientResolutionStatus, {
resolveClass: styles.red,
resolveString: t('enter_crypto_address'),
});
return;
}
@@ -169,6 +182,7 @@ const CryptoAddressSettingContent = ({ account }: { account: ReturnType<typeof u
value={cryptoAddress}
onChange={(e) => {
setCheckedAddress(undefined);
setTransientResolutionStatus(undefined);
setCryptoAddress(e.target.value);
}}
/>
@@ -201,7 +215,7 @@ const CryptoAddressSettingContent = ({ account }: { account: ReturnType<typeof u
<button className={styles.button} onClick={checkCryptoAddress}>
{t('check')}
</button>{' '}
<span className={resolutionStatus.resolveClass}>{resolutionStatus.resolveString}</span>
<span className={(transientResolutionStatus ?? resolutionStatus).resolveClass}>{(transientResolutionStatus ?? resolutionStatus).resolveString}</span>
</div>
</div>
);