Skip to content

Commit

Permalink
feat(extension): display all certificates, votes, and gov actions in …
Browse files Browse the repository at this point in the history
…dapp container
  • Loading branch information
mchappell committed Jul 16, 2024
1 parent 66dd8fd commit c41e3bd
Show file tree
Hide file tree
Showing 8 changed files with 484 additions and 251 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import { useTranslation } from 'react-i18next';
import { Layout } from '../Layout';
import { useViewsFlowContext } from '@providers/ViewFlowProvider';
import styles from './ConfirmTransaction.module.scss';
import { Wallet } from '@lace/cardano';
import { useWalletStore } from '@stores';
import { useDisallowSignTx, useSignWithHardwareWallet, useOnBeforeUnload } from './hooks';
import { getTxType } from './utils';
import { ConfirmTransactionContent } from './ConfirmTransactionContent';
import { TX_CREATION_TYPE_KEY, TxCreationType } from '@providers/AnalyticsProvider/analyticsTracker';
import { txSubmitted$ } from '@providers/AnalyticsProvider/onChain';
import { useAnalyticsContext } from '@providers';
Expand All @@ -21,6 +18,7 @@ import { DAPP_CHANNELS } from '@src/utils/constants';
import { of, take } from 'rxjs';
import { runtime } from 'webextension-polyfill';
import { Skeleton } from 'antd';
import { DappTransactionContainer } from './DappTransactionContainer';

export const ConfirmTransaction = (): React.ReactElement => {
const { t } = useTranslation();
Expand All @@ -29,22 +27,11 @@ export const ConfirmTransaction = (): React.ReactElement => {
setDappInfo,
signTxRequest: { request: req, set: setSignTxRequest }
} = useViewsFlowContext();

const { walletType, isHardwareWallet } = useWalletStore();
const analytics = useAnalyticsContext();
const [confirmTransactionError, setConfirmTransactionError] = useState(false);
const [confirmTransactionError] = useState(false);
const disallowSignTx = useDisallowSignTx(req);
const { isConfirmingTx, signWithHardwareWallet } = useSignWithHardwareWallet(req);
const [txType, setTxType] = useState<Wallet.Cip30TxType>();

useEffect(() => {
const fetchTxType = async () => {
if (!req) return;
const type = await getTxType(req.transaction.toCore());
setTxType(type);
};
fetchTxType();
}, [req]);

const onConfirmTransaction = () => {
analytics.sendEventToPostHog(PostHogAction.SendTransactionSummaryConfirmClick, {
Expand Down Expand Up @@ -96,11 +83,7 @@ export const ConfirmTransaction = (): React.ReactElement => {

return (
<Layout layoutClassname={cn(confirmTransactionError && styles.layoutError)} pageClassname={styles.spaceBetween}>
{req && txType ? (
<ConfirmTransactionContent txType={txType} onError={() => setConfirmTransactionError(true)} />
) : (
<Skeleton loading />
)}
{req ? <DappTransactionContainer /> : <Skeleton loading />}
{!confirmTransactionError && (
<div className={styles.actions}>
<Button
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import React, { useState, useEffect, useMemo } from 'react';
import { useObservable } from '@lace/common';
import { DappTransaction } from '@lace/core';
import {
DappTransaction,
TxDetailsCertificates,
TxDetailsProposalProcedures,
TxDetailsVotingProcedures
} from '@lace/core';
import { Flex } from '@input-output-hk/lace-ui-toolkit';
import { useViewsFlowContext } from '@providers/ViewFlowProvider';

Expand All @@ -26,6 +31,8 @@ import { utxoAndBackendChainHistoryResolver } from '@src/utils/utxo-chain-histor
import { eraSlotDateTime } from '@src/utils/era-slot-datetime';
import { AddressBookSchema, useDbStateValue } from '@lib/storage';
import { getAllWalletsAddresses } from '@src/utils/get-all-wallets-addresses';
import { useCexplorerBaseUrl, useDisallowSignTx } from './hooks';
import { NonRegisteredUserModal } from './NonRegisteredUserModal/NonRegisteredUserModal';

interface DappTransactionContainerProps {
errorMessage?: string;
Expand All @@ -43,8 +50,13 @@ export const DappTransactionContainer = withAddressBookContext(
inMemoryWallet,
blockchainProvider: { assetProvider },
walletUI: { cardanoCoin },
walletState
walletState,
currentChain
} = useWalletStore();
const [isNonRegisteredUserModalVisible, setIsNonRegisteredUserModalVisible] = useState<boolean>(false);
const [userAckNonRegisteredState, setUserAckNonRegisteredState] = useState<boolean>(false);
const disallowSignTx = useDisallowSignTx(req);
const explorerBaseUrl = useCexplorerBaseUrl();

const ownAddresses = useObservable(inMemoryWallet.addresses$)?.map((a) => a.address);
const { list: addressBook } = useAddressBookContext() as useDbStateValue<AddressBookSchema>;
Expand Down Expand Up @@ -78,6 +90,18 @@ export const DappTransactionContainer = withAddressBookContext(
);

const tx = useMemo(() => req?.transaction.toCore(), [req?.transaction]);

useEffect(() => {
if (userAckNonRegisteredState || !tx?.body?.votingProcedures) return () => void 0;
const subscription = inMemoryWallet?.governance?.isRegisteredAsDRep$?.subscribe(
(hasValidDrepRegistration): void => {
setIsNonRegisteredUserModalVisible(!hasValidDrepRegistration);
}
);

return () => subscription?.unsubscribe();
}, [inMemoryWallet?.governance?.isRegisteredAsDRep$, userAckNonRegisteredState, tx]);

const txCollateral = useComputeTxCollateral(walletState, tx);

const userAddresses = useMemo(() => walletInfo.addresses.map((v) => v.address), [walletInfo.addresses]);
Expand Down Expand Up @@ -144,21 +168,51 @@ export const DappTransactionContainer = withAddressBookContext(

return (
<Flex flexDirection="column" justifyContent="space-between" alignItems="stretch">
<NonRegisteredUserModal
visible={isNonRegisteredUserModalVisible}
onConfirm={() => {
setUserAckNonRegisteredState(true);
setIsNonRegisteredUserModalVisible(false);
}}
onClose={() => disallowSignTx(true)}
/>
{req && transactionInspectionDetails && dappInfo ? (
<DappTransaction
fiatCurrencyCode={fiatCurrency?.code}
fiatCurrencyPrice={priceResult?.cardano?.price}
coinSymbol={cardanoCoin.symbol}
txInspectionDetails={transactionInspectionDetails}
dappInfo={dappInfo}
fromAddress={fromAddressTokens}
errorMessage={errorMessage}
toAddress={toAddressTokens}
collateral={txCollateral}
expiresBy={eraSlotDateTime(eraSummaries, tx.body.validityInterval?.invalidHereafter)}
ownAddresses={allWalletsAddresses.length > 0 ? allWalletsAddresses : ownAddresses}
addressToNameMap={addressToNameMap}
/>
<>
<DappTransaction
fiatCurrencyCode={fiatCurrency?.code}
fiatCurrencyPrice={priceResult?.cardano?.price}
coinSymbol={cardanoCoin.symbol}
txInspectionDetails={transactionInspectionDetails}
dappInfo={dappInfo}
fromAddress={fromAddressTokens}
errorMessage={errorMessage}
toAddress={toAddressTokens}
collateral={txCollateral}
expiresBy={eraSlotDateTime(eraSummaries, tx.body.validityInterval?.invalidHereafter)}
ownAddresses={allWalletsAddresses.length > 0 ? allWalletsAddresses : ownAddresses}
addressToNameMap={addressToNameMap}
/>
{tx?.body?.certificates?.length > 0 && (
<TxDetailsCertificates
cardanoCoin={cardanoCoin}
certificates={tx.body.certificates}
chainNetworkId={currentChain.networkId}
/>
)}
{tx?.body?.proposalProcedures?.length > 0 && (
<TxDetailsProposalProcedures
explorerBaseUrl={explorerBaseUrl}
cardanoCoin={cardanoCoin}
proposalProcedures={tx.body.proposalProcedures}
/>
)}
{tx?.body?.votingProcedures?.length > 0 && (
<TxDetailsVotingProcedures
explorerBaseUrl={explorerBaseUrl}
votingProcedures={tx.body.votingProcedures}
/>
)}
</>
) : (
<Skeleton loading />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
/* eslint-disable sonarjs/no-identical-functions */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable import/imports-first */
/* eslint-disable no-magic-numbers */
const mockGetKeyAgentType = jest.fn();
const mockUseWalletStore = jest.fn();
const mockExposeApi = jest.fn(() => ({ shutdown: jest.fn() }));
const mockConfirmTransactionContent = jest.fn(() => <span data-testid="ConfirmTransactionContent" />);
const mockGetTxType = jest.fn();
const mockUseDisallowSignTx = jest.fn();
const mockUseViewsFlowContext = jest.fn();
const mockUseSignWithHardwareWallet = jest.fn();
const mockUseOnBeforeUnload = jest.fn();
const mockUseComputeTxCollateral = jest.fn().mockReturnValue(BigInt(1_000_000));
const mockCreateTxInspector = jest.fn().mockReturnValue(() => ({ minted: [] as any, burned: [] as any }));
import * as React from 'react';
import { cleanup, render, act, fireEvent } from '@testing-library/react';
Expand All @@ -19,6 +20,7 @@ import '@testing-library/jest-dom';
import { BehaviorSubject } from 'rxjs';
import { Wallet } from '@lace/cardano';
import { getWrapper } from '../testing.utils';
import * as UseComputeTxCollateral from '@hooks/useComputeTxCollateral';

const assetInfo$ = new BehaviorSubject(new Map());
const available$ = new BehaviorSubject([]);
Expand All @@ -33,7 +35,11 @@ const inMemoryWallet = {
utxo: {
available$
}
}
},
delegation: {
rewardAccounts$: new BehaviorSubject([])
},
addresses$: new BehaviorSubject([])
};

jest.mock('@src/stores', () => ({
Expand Down Expand Up @@ -73,21 +79,11 @@ jest.mock('@lace/common', () => {
};
});

jest.mock('../ConfirmTransactionContent', () => {
const original = jest.requireActual('../ConfirmTransactionContent');
return {
__esModule: true,
...original,
ConfirmTransactionContent: mockConfirmTransactionContent
};
});

jest.mock('../utils.ts', () => {
const original = jest.requireActual('../utils.ts');
return {
__esModule: true,
...original,
getTxType: mockGetTxType
...original
};
});

Expand All @@ -102,6 +98,11 @@ jest.mock('../hooks.ts', () => {
};
});

jest.mock('@hooks/useComputeTxCollateral', (): typeof UseComputeTxCollateral => ({
...jest.requireActual<typeof UseComputeTxCollateral>('@hooks/useComputeTxCollateral'),
useComputeTxCollateral: mockUseComputeTxCollateral
}));

jest.mock('@providers/ViewFlowProvider', () => {
const original = jest.requireActual('@providers/ViewFlowProvider');
return {
Expand Down Expand Up @@ -146,19 +147,18 @@ describe('Testing ConfirmTransaction component', () => {
test('Should render proper state for inMemory wallet', async () => {
let queryByTestId: any;

const txType = 'txType';
mockGetKeyAgentType.mockReset();
mockGetKeyAgentType.mockReturnValue(Wallet.KeyManagement.KeyAgentType.InMemory);
mockUseWalletStore.mockReset();
mockUseWalletStore.mockImplementation(() => ({
getKeyAgentType: mockGetKeyAgentType,
inMemoryWallet,
walletUI: {},
walletInfo: {},
walletInfo: {
addresses: []
},
blockchainProvider: { assetProvider }
}));
mockGetTxType.mockReset();
mockGetTxType.mockReturnValue(txType);

const signTxData = { tx: { id: 'test-tx-id' } };
const disallowSignTx = jest.fn();
Expand All @@ -184,14 +184,6 @@ describe('Testing ConfirmTransaction component', () => {
}));
});

expect(queryByTestId('ConfirmTransactionContent')).toBeInTheDocument();
expect(mockConfirmTransactionContent).toHaveBeenLastCalledWith(
{
txType,
onError: expect.any(Function)
},
{}
);
expect(mockUseOnBeforeUnload).toHaveBeenCalledWith(disallowSignTx);
expect(queryByTestId(testIds.dappTransactionConfirm)).toHaveTextContent('Confirm');
expect(queryByTestId(testIds.dappTransactionConfirm)).not.toBeDisabled();
Expand Down Expand Up @@ -222,7 +214,9 @@ describe('Testing ConfirmTransaction component', () => {
isHardwareWallet: true,
walletType: 'Ledger',
walletUI: {},
walletInfo: {},
walletInfo: {
addresses: []
},
blockchainProvider: { assetProvider }
}));

Expand Down
Loading

0 comments on commit c41e3bd

Please sign in to comment.