Skip to content

Commit

Permalink
feat: add cancel transaction, ref LEA-958
Browse files Browse the repository at this point in the history
  • Loading branch information
alter-eggo committed Oct 30, 2024
1 parent fe2a20b commit 7e16463
Show file tree
Hide file tree
Showing 26 changed files with 704 additions and 241 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
"@coinbase/cbpay-js": "2.1.0",
"@fungible-systems/zone-file": "2.0.0",
"@hirosystems/token-metadata-api-client": "1.2.0",
"@hookform/resolvers": "3.9.0",
"@leather.io/bitcoin": "0.14.2",
"@leather.io/constants": "0.12.5",
"@leather.io/crypto": "1.6.6",
Expand Down Expand Up @@ -234,6 +235,7 @@
"react-dom": "18.3.1",
"react-dom-confetti": "0.2.0",
"react-head": "3.4.2",
"react-hook-form": "7.53.1",
"react-intersection-observer": "9.5.2",
"react-lottie": "1.2.4",
"react-qr-code": "2.0.12",
Expand Down
25 changes: 25 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 1 addition & 15 deletions src/app/common/hooks/use-submit-stx-transaction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useCallback } from 'react';

import { bytesToHex } from '@stacks/common';
import { StacksTransaction, broadcastTransaction } from '@stacks/transactions';

import { delay, isError } from '@leather.io/utils';
Expand All @@ -14,7 +13,6 @@ import { useLoading } from '@app/common/hooks/use-loading';
import { safelyFormatHexTxid } from '@app/common/utils/safe-handle-txid';
import { useToast } from '@app/features/toasts/use-toast';
import { useCurrentStacksNetworkState } from '@app/store/networks/networks.hooks';
import { useSubmittedTransactionsActions } from '@app/store/submitted-transactions/submitted-transactions.hooks';

const timeForApiToUpdate = 250;

Expand All @@ -27,7 +25,6 @@ interface UseSubmitTransactionCallbackArgs {
onError(error: Error | string): void;
}
export function useSubmitTransactionCallback({ loadingKey }: UseSubmitTransactionArgs) {
const submittedTransactionsActions = useSubmittedTransactionsActions();
const toast = useToast();
const refreshAccountData = useRefreshAllAccountData();

Expand All @@ -47,10 +44,6 @@ export function useSubmitTransactionCallback({ loadingKey }: UseSubmitTransactio
setIsIdle();
} else {
logger.info('Transaction broadcast', response);
submittedTransactionsActions.newTransactionSubmitted({
rawTx: bytesToHex(transaction.serialize()),
txid: safelyFormatHexTxid(response.txid),
});

await delay(500);

Expand All @@ -65,13 +58,6 @@ export function useSubmitTransactionCallback({ loadingKey }: UseSubmitTransactio
setIsIdle();
}
},
[
setIsLoading,
stacksNetwork,
toast,
setIsIdle,
submittedTransactionsActions,
refreshAccountData,
]
[setIsLoading, stacksNetwork, toast, setIsIdle, refreshAccountData]
);
}
5 changes: 5 additions & 0 deletions src/app/common/transactions/stacks/transaction.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,8 @@ export function getTxSenderAddress(tx: StacksTransaction): string | undefined {
export function isPendingTx(tx: StacksTx) {
return tx.tx_status === 'pending';
}

export enum StacksTransactionActionType {
Cancel = 'cancel',
IncreaseFee = 'increase-fee',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { ActivitySelectors } from '@tests/selectors/activity.selectors';
import { css } from 'leather-styles/css';
import { HStack, styled } from 'leather-styles/jsx';

import { ChevronDownIcon, ChevronsRightIcon, CloseIcon, DropdownMenu, Flag } from '@leather.io/ui';

interface StacksTransactionActionMenuProps {
onIncreaseFee(): void;
onCancelTransaction(): void;
}

export function StacksTransactionActionMenu({
onIncreaseFee,
onCancelTransaction,
}: StacksTransactionActionMenuProps) {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger
data-testid={ActivitySelectors.ActivityItemMenuBtn}
className={css({
zIndex: 10,
borderRadius: 'sm',
px: 'space.01',
_hover: {
background: 'ink.component-background-hover',
},
})}
>
<Flag spacing="space.02" reverse img={<ChevronDownIcon variant="small" />}>
<styled.span textStyle="label.03">Options</styled.span>
</Flag>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
align="end"
side="bottom"
sideOffset={8}
className={css({
zIndex: 100,
width: 'settingsMenuWidth',
})}
>
<DropdownMenu.Group>
<DropdownMenu.Item
data-testid={ActivitySelectors.ActivityItemMenuIncreaseFee}
onClick={e => {
e.stopPropagation();
onIncreaseFee();
}}
>
<HStack>
<ChevronsRightIcon />
<styled.span textStyle="label.02">Increase fee</styled.span>
</HStack>
</DropdownMenu.Item>
<DropdownMenu.Item
data-testid={ActivitySelectors.ActivityItemMenuCancelTransaction}
onClick={e => {
e.stopPropagation();
onCancelTransaction();
}}
>
<HStack>
<CloseIcon />
<styled.span textStyle="label.02">Cancel transaction</styled.span>
</HStack>
</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useLocation, useNavigate } from 'react-router-dom';
import { useMatch, useNavigate } from 'react-router-dom';

import { StacksTx } from '@leather.io/models';

Expand All @@ -7,6 +7,7 @@ import { analytics } from '@shared/utils/analytics';

import { useStacksExplorerLink } from '@app/common/hooks/use-stacks-explorer-link';
import {
StacksTransactionActionType,
getTxCaption,
getTxTitle,
getTxValue,
Expand All @@ -20,7 +21,7 @@ import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/s
import { useIsPrivateMode } from '@app/store/settings/settings.selectors';

import { TransactionItemLayout } from '../transaction-item/transaction-item.layout';
import { IncreaseFeeButton } from './increase-fee-button';
import { StacksTransactionActionMenu } from './stacks-transaction-action-menu';
import { StacksTransactionIcon } from './stacks-transaction-icon';
import { StacksTransactionStatus } from './stacks-transaction-status';

Expand All @@ -44,11 +45,14 @@ export function StacksTransactionItem({
const currentAccount = useCurrentStacksAccount();
const isPrivate = useIsPrivateMode();

const { pathname } = useLocation();
const navigate = useNavigate();
const { whenWallet } = useWalletType();

const cancelTransactionMatch = useMatch(RouteUrls.CancelStacksTransaction);
const increaseFeeMatch = useMatch(RouteUrls.IncreaseStacksFee);

const hasTransferDetailsData = !!caption && !!title && !!value && !!link;

if (!transaction && !hasTransferDetailsData) return null;

const openTxLink = () => {
Expand All @@ -58,10 +62,21 @@ export function StacksTransactionItem({
});
};

const onIncreaseFee = () => {
const isOriginator = transaction?.sender_address === currentAccount?.address;
const isPending = transaction && isPendingTx(transaction);

const txCaption = transaction ? getTxCaption(transaction) : caption || '';
const txIcon = transaction ? <StacksTransactionIcon transaction={transaction} /> : icon;
const txTitle = transaction ? getTxTitle(transaction) : title || '';
const txValue = transaction ? getTxValue(transaction, isOriginator) : value;

function handleTransactionAction(action: StacksTransactionActionType) {
if (!transaction) return;

const routeUrl = RouteUrls.IncreaseStxFee.replace(':txid', transaction.tx_id);
const routeUrl =
action === StacksTransactionActionType.IncreaseFee
? RouteUrls.IncreaseStacksFee.replace(':txid', transaction.tx_id)
: RouteUrls.CancelStacksTransaction.replace(':txid', transaction.tx_id);

whenWallet({
ledger: () =>
Expand All @@ -71,28 +86,24 @@ export function StacksTransactionItem({
})(),
software: () => navigate(routeUrl),
})();
};
}

const isOriginator = transaction?.sender_address === currentAccount?.address;
const isPending = transaction && isPendingTx(transaction);
const isTransactionActionRoute = !!cancelTransactionMatch || !!increaseFeeMatch;

const txCaption = transaction ? getTxCaption(transaction) : caption || '';
const txIcon = transaction ? <StacksTransactionIcon transaction={transaction} /> : icon;
const txTitle = transaction ? getTxTitle(transaction) : title || '';
const txValue = transaction ? getTxValue(transaction, isOriginator) : value;
const increaseFeeButton = (
<IncreaseFeeButton
isEnabled={isOriginator && isPending}
isSelected={pathname === RouteUrls.IncreaseStxFee}
onIncreaseFee={onIncreaseFee}
/>
);
const txStatus = transaction && <StacksTransactionStatus transaction={transaction} />;

const rightElement =
isOriginator && isPending && !isTransactionActionRoute ? (
<StacksTransactionActionMenu
onIncreaseFee={() => handleTransactionAction(StacksTransactionActionType.IncreaseFee)}
onCancelTransaction={() => handleTransactionAction(StacksTransactionActionType.Cancel)}
/>
) : undefined;

return (
<TransactionItemLayout
openTxLink={openTxLink}
rightElement={isOriginator && isPending ? increaseFeeButton : undefined}
rightElement={rightElement}
txCaption={txCaption}
txIcon={txIcon}
txStatus={txStatus}
Expand Down
7 changes: 6 additions & 1 deletion src/app/features/activity-list/components/tab-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ActivitySelectors } from '@tests/selectors/activity.selectors';
import { Box } from 'leather-styles/jsx';

interface ActivityListTabWrapperProps {
Expand All @@ -10,7 +11,11 @@ export function ActivityListTabWrapper({
padContent = false,
}: ActivityListTabWrapperProps) {
return (
<Box minHeight="dialogContentHeight" py={padContent ? 'space.11' : undefined}>
<Box
minHeight="dialogContentHeight"
py={padContent ? 'space.11' : undefined}
data-testid={ActivitySelectors.ActivityList}
>
{children}
</Box>
);
Expand Down

This file was deleted.

Loading

0 comments on commit 7e16463

Please sign in to comment.