Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { ReactNode, useCallback, useEffect } from 'react';
import { Image } from 'antd';
import { useTranslation } from 'react-i18next';
import Empty from '../../../assets/icons/empty.svg';
import styles from './Layout.module.scss';
import { Button } from '@lace/common';

type DappErrorProps = {
title: string;
description: ReactNode;
closeButtonLabel?: string;
onCloseClick?: () => void;
onMount?: () => void;
containerTestId: string;
imageTestId: string;
titleTestId: string;
descriptionTestId: string;
closeButtonTestId: string;
};
export const DappError = ({
title,
description,
closeButtonLabel,
onCloseClick,
onMount,
containerTestId,
imageTestId,
titleTestId,
descriptionTestId,
closeButtonTestId
}: DappErrorProps): React.ReactElement => {
const { t } = useTranslation();
const handleClose = useCallback(() => {
if (onCloseClick) onCloseClick();
window.close();
}, [onCloseClick]);

useEffect(() => {
if (onMount) onMount();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<div data-testid={containerTestId} className={styles.dappErrorContainer}>
<div className={styles.dappErrorContent}>
<Image data-testid={imageTestId} preview={false} width={112} src={Empty} />
<div className={styles.heading} data-testid={titleTestId}>
{title}
</div>
<div className={styles.description} data-testid={descriptionTestId}>
{description}
</div>
</div>
<div className={styles.footer}>
<Button data-testid={closeButtonTestId} className={styles.footerBtn} onClick={handleClose}>
{closeButtonLabel || t('dapp.dappErrorPage.closeButton')}
</Button>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export const DappTransactionFail = (): React.ReactElement => {
}, [analytics]);

return (
<div data-testid="dapp-sign-tx-fail" className={styles.noWalletContainer}>
<div className={styles.noWalletContent}>
<div data-testid="dapp-sign-tx-fail" className={styles.dappErrorContainer}>
<div className={styles.dappErrorContent}>
<Image data-testid="dapp-sign-tx-fail-image" preview={false} width={112} src={Fail} />
<div className={styles.heading}>{t('dapp.sign.failure.title')}</div>
<div className={styles.description}>{t('dapp.sign.failure.description')}</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const DappTransactionSuccess = (): React.ReactElement => {
}, [analytics]);

return (
<div data-testid="dapp-sign-tx-success" className={styles.noWalletContainer}>
<div className={styles.noWalletContent}>
<div data-testid="dapp-sign-tx-success" className={styles.dappErrorContainer}>
<div className={styles.dappErrorContent}>
<Image data-testid="dapp-sign-tx-success-image" preview={false} width={112} src={Success} />
<div data-testid="dapp-sign-tx-success-heading" className={styles.heading}>
{t('browserView.transaction.success.youCanSafelyCloseThisPanel')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@
padding-top: size_unit(4);
}

.noWalletContainer {
.dappErrorContainer {
align-items: center;
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-between;
width: 100%;

.noWalletContent {
.dappErrorContent {
padding: 0 size_unit(3);
display: flex;
flex: 1;
Expand All @@ -45,7 +45,7 @@
.heading {
color: var(--text-color-secondary);
font-size: var(--bodyLarge);
font-weight: 800;
font-weight: 600;
letter-spacing: 0.02em;
line-height: size_unit(3);
margin-top: size_unit(2);
Expand All @@ -55,6 +55,7 @@
.description {
color: var(--text-color-secondary);
font-size: var(--bodySmall);
font-weight: 500;
letter-spacing: 0.02em;
line-height: size_unit(3);
margin-top: size_unit(2);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,34 +1,76 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ConfirmDRepRetirement } from '@lace/core';
import { SignTxData } from './types';
import { certificateInspectorFactory, drepIDasBech32FromHash, getOwnRetirementMessageKey } from './utils';
import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils';
import { Wallet } from '@lace/cardano';
import { useWalletStore } from '@src/stores';
import { useIsOwnPubDRepKey } from './hooks';

const { CertificateType } = Wallet.Cardano;
import { SignTxData } from './types';
import { useGetOwnPubDRepKeyHash } from './hooks';
import { Skeleton } from 'antd';
import { exposeApi, RemoteApiPropertyType } from '@cardano-sdk/web-extension';
import { UserPromptService } from '@lib/scripts/background/services';
import { of } from 'rxjs';
import { ApiError, APIErrorCode } from '@cardano-sdk/dapp-connector';
import { DAPP_CHANNELS } from '@utils/constants';
import { runtime } from 'webextension-polyfill';
import { DappError } from '../DappError';

interface Props {
signTxData: SignTxData;
errorMessage?: string;
onError: () => void;
}

export const ConfirmDRepRetirementContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => {
export const disallowSignTx = (): void => {
exposeApi<Pick<UserPromptService, 'allowSignTx'>>(
{
api$: of({
async allowSignTx(): Promise<boolean> {
return Promise.reject(new ApiError(APIErrorCode.InvalidRequest, 'DRep ID mismatch'));
}
}),
baseChannel: DAPP_CHANNELS.userPrompt,
properties: { allowSignTx: RemoteApiPropertyType.MethodReturningPromise }
},
{ logger: console, runtime }
);
};

export const ConfirmDRepRetirementContainer = ({ signTxData, onError, errorMessage }: Props): React.ReactElement => {
const { t } = useTranslation();
const {
walletUI: { cardanoCoin },
inMemoryWallet
walletUI: { cardanoCoin }
} = useWalletStore();
const certificate = certificateInspectorFactory<Wallet.Cardano.UnRegisterDelegateRepresentativeCertificate>(
CertificateType.UnregisterDelegateRepresentative
Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative
)(signTxData.tx);
const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${
cardanoCoin.symbol
}`;
const { loading: loadingOwnPubDRepKeyHash, ownPubDRepKeyHash } = useGetOwnPubDRepKeyHash();
const isNotOwnDRepKey = certificate.dRepCredential.hash !== ownPubDRepKeyHash;

if (loadingOwnPubDRepKeyHash) {
return <Skeleton />;
}

const isOwnRetirement = useIsOwnPubDRepKey(inMemoryWallet.getPubDRepKey, certificate.dRepCredential.hash);
const ownRetirementMessageKey = getOwnRetirementMessageKey(isOwnRetirement);
if (isNotOwnDRepKey) {
return (
<DappError
title={t('core.DRepRetirement.drepIdMismatchScreen.title')}
description={t('core.DRepRetirement.drepIdMismatchScreen.description')}
onMount={() => {
disallowSignTx();
onError();
}}
containerTestId="drep-id-mismatch-container"
imageTestId="drep-id-mismatch-image"
titleTestId="drep-id-mismatch-heading"
descriptionTestId="drep-id-mismatch-description"
closeButtonTestId="drep-id-mismatch-close-button"
/>
);
}

return (
<ConfirmDRepRetirement
Expand All @@ -44,7 +86,7 @@ export const ConfirmDRepRetirementContainer = ({ signTxData, errorMessage }: Pro
drepId: t('core.DRepRetirement.drepId')
}
}}
errorMessage={errorMessage || t(ownRetirementMessageKey)}
errorMessage={errorMessage}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
@import '../../../../../../../packages/common/src/ui/styles/theme.scss';
@import '../../../../../src/styles/rules/flex.scss';

.actions {
display: flex;
gap: size_unit(1);
justify-content: space-evenly;
flex-direction: column;
}

.spaceBetween {
justify-content: space-between;
padding-top: size_unit(2);
}

.layoutError {
padding: 0;
}

.actions {
background-color: var(--bg-color-body);
@extend %flex-column;
justify-content: center;
background-color: var(--bg-color-body);
gap: size_unit(1);
padding: size_unit(2) size_unit(3) size_unit(2) size_unit(3);
border-top: 2px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey));
margin: size_unit(4) size_unit(-3) size_unit(-2) size_unit(-3);
position: sticky;
bottom: 0;
z-index: 10;
.actionBtn {
width: 100%;
}
z-index: 10;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-console */
import React, { useMemo } from 'react';
import React, { useMemo, useState } from 'react';
import cn from 'classnames';
import { Button, PostHogAction } from '@lace/common';
import { useTranslation } from 'react-i18next';
import { Layout } from '../Layout';
Expand Down Expand Up @@ -38,7 +39,8 @@ export const ConfirmTransaction = (): React.ReactElement => {
);
const { getKeyAgentType } = useWalletStore();
const analytics = useAnalyticsContext();
const { signTxData, errorMessage } = useSignTxData(dappDataApi.getSignTxData);
const { signTxData, errorMessage: getSignTxDataError } = useSignTxData(dappDataApi.getSignTxData);
const [confirmTransactionError, setConfirmTransactionError] = useState(false);
const keyAgentType = getKeyAgentType();
const isUsingHardwareWallet = keyAgentType !== Wallet.KeyManagement.KeyAgentType.InMemory;
const disallowSignTx = useDisallowSignTx();
Expand All @@ -62,29 +64,40 @@ export const ConfirmTransaction = (): React.ReactElement => {
useOnBeforeUnload(disallowSignTx);

return (
<Layout pageClassname={styles.spaceBetween} title={title}>
<ConfirmTransactionContent txType={txType} signTxData={signTxData} errorMessage={errorMessage} />
<div className={styles.actions}>
<Button
onClick={onConfirm}
disabled={!!errorMessage}
loading={isUsingHardwareWallet && isConfirmingTx}
data-testid="dapp-transaction-confirm"
className={styles.actionBtn}
>
{isUsingHardwareWallet
? t('browserView.transaction.send.footer.confirmWithDevice', { hardwareWallet: keyAgentType })
: t('dapp.confirm.btn.confirm')}
</Button>
<Button
color="secondary"
data-testid="dapp-transaction-cancel"
onClick={() => disallowSignTx(true)}
className={styles.actionBtn}
>
{t('dapp.confirm.btn.cancel')}
</Button>
</div>
<Layout
layoutClassname={cn(confirmTransactionError && styles.layoutError)}
pageClassname={styles.spaceBetween}
title={!confirmTransactionError && title}
>
<ConfirmTransactionContent
txType={txType}
signTxData={signTxData}
onError={() => setConfirmTransactionError(true)}
errorMessage={getSignTxDataError}
/>
{!confirmTransactionError && (
<div className={styles.actions}>
<Button
onClick={onConfirm}
disabled={!!getSignTxDataError}
loading={isUsingHardwareWallet && isConfirmingTx}
data-testid="dapp-transaction-confirm"
className={styles.actionBtn}
>
{isUsingHardwareWallet
? t('browserView.transaction.send.footer.confirmWithDevice', { hardwareWallet: keyAgentType })
: t('dapp.confirm.btn.confirm')}
</Button>
<Button
color="secondary"
data-testid="dapp-transaction-cancel"
onClick={() => disallowSignTx(true)}
className={styles.actionBtn}
>
{t('dapp.confirm.btn.cancel')}
</Button>
</div>
)}
</Layout>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ interface Props {
txType?: Wallet.Cip30TxType;
signTxData?: SignTxData;
errorMessage?: string;
onError?: () => void;
}

export const ConfirmTransactionContent = ({ txType, signTxData, errorMessage }: Props): React.ReactElement => {
export const ConfirmTransactionContent = ({ txType, signTxData, onError, errorMessage }: Props): React.ReactElement => {
if (!signTxData) {
return <Skeleton loading />;
}
if (txType === Wallet.Cip30TxType.DRepRegistration) {
return <ConfirmDRepRegistrationContainer signTxData={signTxData} errorMessage={errorMessage} />;
}
if (txType === Wallet.Cip30TxType.DRepRetirement) {
return <ConfirmDRepRetirementContainer signTxData={signTxData} errorMessage={errorMessage} />;
return <ConfirmDRepRetirementContainer signTxData={signTxData} onError={onError} errorMessage={errorMessage} />;
}
if (txType === Wallet.Cip30TxType.DRepUpdate) {
return <ConfirmDRepUpdateContainer signTxData={signTxData} errorMessage={errorMessage} />;
Expand Down
Loading