Skip to content

Commit

Permalink
[ENG-3199] feat: Make UI for RBF in web-extension (#656)
Browse files Browse the repository at this point in the history
* [ENG-3199] feat: Make UI for RBF in web-extension

* Add a display condition for Accelerate tx button

* Add `Speed up transaction` modal

* Add custom toast styling for `success` option

* Add ledger tx confirmation modal for the RBF

* Update the ledger UI for RBF and Speed up button icon

* Transform the Speed up modal into a separate screen

* Add toaster bottom indent back

* Update the speed up tx page with new buttons

* Add the custom fee modal to the tx speed up

* Improve speed up tx UI

* Add `Insufficient funds` warning text and disable the speed up option, update the `xverse-core` version

* Add rbf recommended fees fetching

* Improve the tx speed up logic

* Improve the tx speed up logic

* Add `useTransaction` hook and tx speed up submit

* Fix accountType and fee buttons order

* Fix accountType assignment

* Refactor fee rate calculation

* Remove unused imports

* Change the numbers displaying logic on the speed up tx screen

* Convert the `estimatedCompletionTime` var into a function

* Update the speed up tx option info alignment

* Fix accountId property

* Add rbf tx broadcasting

* Update @secretkeylabs/xverse-core version

* Move the ledger confirmation UI to the speed up tx screen

* Update the ledger confirmation logic for rbf

* Update handleBackButtonClick to handleGoBack

* Remove the unused imports

* Update the rbf custom fee logic

* Add the total fee calculation for the custom fee using `getRbfFeeSummary` func

* Move speed up tx screen styles to a separate file

* Fix the custom total fee calculation

* Improve the custom fee calculation

* Improve the custom fee calculation

* Add `isTransactionRbfEnabled` check

* Update the xverse-core version

* Update the xverse-core version

* Hide rbf for brc20, add estimated time for the custom fee

* Update the xverse-core version

* Remove console logs and add err catching on broadcasting

* Update the xverse-core version

* chore: fix type errors after ActionButton interface change

* Update the `isTransactionRbfEnabled` func usage and xverse-core version

* Update the xverse-core version

* Update the useEffect calling the `fetchRbfData` func

* Make some code fixes according to PR comments

* Update the import from xverse-core and the version

* Update the useEffect deps

* Fix the custom fee logic

* Use new RBF core logic

* use mempool api

* fix merge issue

* update core

* chore: comment breaking changes

* Improve the custom fee logic

* Don't show speed up if disabled in settings

* Update core version

* Add key to groupedTxs

* Temp fix for multiple transactions appearing after RBF

* Navigate directly to homepage instead of relative history count

* fix: include xverse-core with rbf fixes

* chore: bump core version to include chain rbf fixes

* fix: update missing btcClients in rbf screens

---------

Co-authored-by: Tim Man <tim@secretkeylabs.com>
Co-authored-by: victorkirov <victor.kirov@gmail.com>
Co-authored-by: fede erbes <fedeerbes@gmail.com>
Co-authored-by: jordankzf <jordan@secretkeylabs.com>
  • Loading branch information
5 people committed Dec 7, 2023
1 parent 0188b2c commit eed2090
Show file tree
Hide file tree
Showing 25 changed files with 1,150 additions and 57 deletions.
19 changes: 18 additions & 1 deletion src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import LoadingScreen from '@components/loadingScreen';
import { CheckCircle } from '@phosphor-icons/react';
import rootStore from '@stores/index';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
Expand Down Expand Up @@ -40,7 +41,23 @@ function App(): JSX.Element {
<SessionGuard>
<ThemeProvider theme={Theme}>
<RouterProvider router={router} />
<Toaster position="bottom-center" />
<Toaster
position="bottom-center"
containerStyle={{ bottom: 80 }}
toastOptions={{
success: {
icon: <CheckCircle size={20} weight="bold" />,
style: {
...Theme.typography.body_medium_m,
backgroundColor: Theme.colors.success_medium,
borderRadius: Theme.radius(2),
padding: Theme.spacing(4),
paddingLeft: Theme.spacing(6),
color: Theme.colors.elevation0,
},
},
}}
/>
</ThemeProvider>
</SessionGuard>
</PersistGate>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/bottomModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Modal from 'react-modal';
import styled, { useTheme } from 'styled-components';

const BottomModalHeaderText = styled.h1((props) => ({
...props.theme.body_bold_m,
...props.theme.typography.body_bold_l,
flex: 1,
}));

Expand Down
6 changes: 3 additions & 3 deletions src/app/components/button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ interface Props {
icon?: JSX.Element;
iconPosition?: 'left' | 'right';
text: string;
onPress: () => void;
onPress: (e: React.MouseEvent<HTMLButtonElement>) => void;
processing?: boolean;
disabled?: boolean;
transparent?: boolean;
Expand All @@ -102,9 +102,9 @@ function ActionButton({
warning,
hoverDialogId,
}: Props) {
const handleOnPress = () => {
const handleOnPress = (e: React.MouseEvent<HTMLButtonElement>) => {
if (!disabled) {
onPress();
onPress(e);
}
};

Expand Down
13 changes: 5 additions & 8 deletions src/app/components/transactionSetting/editFee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ const InputContainer = styled.div<InputContainerProps>((props) => ({
}`,
backgroundColor: props.theme.colors.elevation1,
borderRadius: props.theme.radius(1),
paddingLeft: props.theme.spacing(5),
paddingRight: props.theme.spacing(5),
paddingTop: props.theme.spacing(5),
paddingBottom: props.theme.spacing(5),
padding: props.theme.spacing(5),
}));

const InputField = styled.input((props) => ({
Expand Down Expand Up @@ -103,7 +100,7 @@ const FeeButton = styled.button<ButtonProps>((props) => ({
color: `${props.isSelected ? props.theme.colors.elevation2 : props.theme.colors.white_400}`,
background: `${props.isSelected ? props.theme.colors.white : 'transparent'}`,
border: `1px solid ${props.isSelected ? 'transparent' : props.theme.colors.elevation6}`,
borderRadius: 40,
borderRadius: props.theme.radius(9),
width: props.isBtc ? 104 : 82,
height: 40,
display: 'flex',
Expand All @@ -112,13 +109,13 @@ const FeeButton = styled.button<ButtonProps>((props) => ({
marginRight: props.isLastInRow ? 0 : 8,
}));

const ButtonContainer = styled.div({
const ButtonContainer = styled.div((props) => ({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginTop: 12,
});
marginTop: props.theme.spacing(6),
}));

const FeeContainer = styled.div({
display: 'flex',
Expand Down
1 change: 1 addition & 0 deletions src/app/components/transactionSetting/editNonce.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ interface Props {
function EditNonce({ nonce, setNonce }: Props) {
const { t } = useTranslation('translation', { keyPrefix: 'TRANSACTION_SETTING' });
const [nonceInput, setNonceInput] = useState(nonce);

const onInputEditNonceChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNonceInput(e.target.value);
};
Expand Down
86 changes: 68 additions & 18 deletions src/app/components/transactions/btcTransaction.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import ActionButton from '@components/button';
import useWalletSelector from '@hooks/useWalletSelector';
import { Brc20HistoryTransactionData, BtcTransactionData } from '@secretkeylabs/xverse-core';
import { FastForward } from '@phosphor-icons/react';
import {
Brc20HistoryTransactionData,
BtcTransactionData,
rbf,
RBFProps,
} from '@secretkeylabs/xverse-core';
import { getBtcTxStatusUrl } from '@utils/helper';
import { isBtcTransaction } from '@utils/transactions/transactions';
import { useCallback } from 'react';
import styled from 'styled-components';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import styled, { useTheme } from 'styled-components';
import TransactionAmount from './transactionAmount';
import TransactionRecipient from './transactionRecipient';
import TransactionStatusIcon from './transactionStatusIcon';
import TransactionTitle from './transactionTitle';

interface TransactionHistoryItemProps {
transaction: BtcTransactionData | Brc20HistoryTransactionData;
}

const TransactionContainer = styled.button((props) => ({
display: 'flex',
alignItems: 'center',
width: '100%',
paddingTop: props.theme.spacing(5),
paddingBottom: props.theme.spacing(5),
padding: props.theme.spacing(5),
paddingLeft: props.theme.spacing(8),
paddingRight: props.theme.spacing(8),
background: 'none',
Expand All @@ -30,10 +35,11 @@ const TransactionContainer = styled.button((props) => ({
}));

const TransactionAmountContainer = styled.div({
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
flex: 1,
width: '100%',
justifyContent: 'flex-end',
});

const TransactionInfoContainer = styled.div((props) => ({
Expand All @@ -47,30 +53,74 @@ const TransactionRow = styled.div((props) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
...props.theme.body_bold_m,
...props.theme.typography.body_bold_m,
}));

const StyledButton = styled(ActionButton)((props) => ({
padding: 0,
border: 'none',
width: 'auto',
height: 'auto',
div: {
...props.theme.typography.body_medium_m,
color: props.theme.colors.tangerine,
},
':hover:enabled': {
backgroundColor: 'transparent',
},
':active:enabled': {
backgroundColor: 'transparent',
},
}));

export default function BtcTransactionHistoryItem(props: TransactionHistoryItemProps) {
const { transaction } = props;
const { network } = useWalletSelector();
interface TransactionHistoryItemProps {
transaction: BtcTransactionData | Brc20HistoryTransactionData;
wallet: RBFProps;
}
export default function BtcTransactionHistoryItem({
transaction,
wallet,
}: TransactionHistoryItemProps) {
const { network, hasActivatedRBFKey } = useWalletSelector();
const isBtc = isBtcTransaction(transaction) ? 'BTC' : 'brc20';
const theme = useTheme();
const { t } = useTranslation('translation', { keyPrefix: 'COIN_DASHBOARD_SCREEN' });

const openBtcTxStatusLink = useCallback(() => {
window.open(getBtcTxStatusUrl(transaction.txid, network), '_blank', 'noopener,noreferrer');
}, []);

const showAccelerateButton =
hasActivatedRBFKey &&
isBtcTransaction(transaction) &&
rbf.isTransactionRbfEnabled(transaction, wallet);

return (
<TransactionContainer onClick={openBtcTxStatusLink}>
<TransactionStatusIcon transaction={transaction} currency={isBtc} />
<TransactionInfoContainer>
<TransactionRow>
<TransactionTitle transaction={transaction} />
<div>
<TransactionTitle transaction={transaction} />
<TransactionRecipient transaction={transaction} />
</div>
<TransactionAmountContainer>
<TransactionAmount transaction={transaction} coin={isBtc} />
{showAccelerateButton && (
<Link to={`/speed-up-tx/${transaction.txid}`}>
<StyledButton
transparent
text={t('SPEED_UP')}
onPress={(e) => {
e.stopPropagation();
}}
icon={<FastForward size={16} color={theme.colors.tangerine} weight="fill" />}
iconPosition="right"
/>
</Link>
)}
</TransactionAmountContainer>
</TransactionRow>
<TransactionRow>
<TransactionRecipient transaction={transaction} />
</TransactionRow>
</TransactionInfoContainer>
</TransactionContainer>
);
Expand Down
7 changes: 4 additions & 3 deletions src/app/components/transactions/transactionTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ export default function TransactionTitle(props: TransactionTitleProps) {
const { transaction } = props;
const { t } = useTranslation('translation', { keyPrefix: 'COIN_DASHBOARD_SCREEN' });
const { coins } = useWalletSelector();
const isPending = transaction.txStatus === 'pending';

const getTokenTransferTitle = (tx: TransactionData): string => {
if (tx.txStatus === 'pending') {
if (isPending) {
return tx.incoming ? t('TRANSACTION_PENDING_RECEIVING') : t('TRANSACTION_PENDING_SENDING');
}
return tx.incoming ? t('TRANSACTION_RECEIVED') : t('TRANSACTION_SENT');
};

const getBrc20TokenTitle = (tx: Brc20HistoryTransactionData): string => {
if (tx.txStatus === 'pending') {
if (isPending) {
return tx.incoming ? t('TRANSACTION_PENDING_RECEIVING') : t('TRANSACTION_PENDING_SENDING');
}
if (tx.operation === 'transfer_send') {
Expand All @@ -48,7 +49,7 @@ export default function TransactionTitle(props: TransactionTitleProps) {
};

const getBtcTokenTransferTitle = (tx: BtcTransactionData): string => {
if (tx.txStatus === 'pending') {
if (isPending) {
if (tx.isOrdinal) {
return tx.incoming
? t('ORDINAL_TRANSACTION_PENDING_RECEIVING')
Expand Down
29 changes: 29 additions & 0 deletions src/app/hooks/queries/useTransaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import useBtcClient from '@hooks/useBtcClient';
import useWalletSelector from '@hooks/useWalletSelector';
import { fetchBtcTransaction } from '@secretkeylabs/xverse-core';
import { useQuery } from '@tanstack/react-query';

export default function useTransaction(id: string) {
const { selectedAccount } = useWalletSelector();
const btcClient = useBtcClient();

const fetchTransaction = async () => {
if (!selectedAccount || !id) {
return;
}

const transaction = await fetchBtcTransaction(
id,
selectedAccount.btcAddress,
selectedAccount.ordinalsAddress,
btcClient,
);

return transaction;
};

return useQuery({
queryKey: ['transaction', id],
queryFn: fetchTransaction,
});
}
1 change: 0 additions & 1 deletion src/app/hooks/useSponsoredTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const useSponsorInfoQuery = (sponsorUrl?: string) =>

export const useSponsoredTransaction = (isSponsorOptionSelected: boolean, sponsorUrl?: string) => {
const [isServiceRunning, setIsServiceRunning] = useState(false);

const { error, data: isActive, isLoading } = useSponsorInfoQuery(sponsorUrl);

useEffect(() => {
Expand Down
5 changes: 5 additions & 0 deletions src/app/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import PrivacyPreferencesScreen from '@screens/settings/privacyPreferences';
import SignatureRequest from '@screens/signatureRequest';
import SignBatchPsbtRequest from '@screens/signBatchPsbtRequest';
import SignPsbtRequest from '@screens/signPsbtRequest';
import SpeedUpTransactionScreen from '@screens/speedUpTransaction';
import Stacking from '@screens/stacking';
import SwapScreen from '@screens/swap';
import SwapConfirmScreen from '@screens/swap/swapConfirmation';
Expand Down Expand Up @@ -262,6 +263,10 @@ const router = createHashRouter([
</AuthGuard>
),
},
{
path: 'speed-up-tx/:id',
element: <SpeedUpTransactionScreen />,
},
{
path: 'create-inscription',
element: (
Expand Down
Loading

0 comments on commit eed2090

Please sign in to comment.