Skip to content

Commit

Permalink
Merge pull request #741 from secretkeylabs/release/v0.28.0
Browse files Browse the repository at this point in the history
release: v0.28.0 to main
  • Loading branch information
teebszet committed Jan 16, 2024
2 parents 19d79bc + f045814 commit ab80d89
Show file tree
Hide file tree
Showing 102 changed files with 5,327 additions and 2,383 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jobs:
ARTIFACT_URL="https://github.com/$OWNER/$REPO/actions/runs/$WORKFLOW_ID"
echo "ARTIFACT_URL=$ARTIFACT_URL" >> $GITHUB_ENV
- name: Delete old bot comments
env:
PR_ID: ${{ github.event.pull_request.number }}
run: |
gh api \
Expand Down
580 changes: 391 additions & 189 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "xverse-web-extension",
"description": "A Bitcoin wallet for Web3",
"version": "0.27.0",
"version": "0.28.0",
"private": true,
"engines": {
"node": "^18.18.2"
Expand All @@ -10,7 +10,8 @@
"@ledgerhq/hw-transport-webusb": "^6.27.13",
"@phosphor-icons/react": "^2.0.10",
"@react-spring/web": "^9.6.1",
"@secretkeylabs/xverse-core": "7.0.0",
"@scure/btc-signer": "^1.1.1",
"@secretkeylabs/xverse-core": "8.0.1",
"@stacks/connect": "7.4.1",
"@stacks/stacks-blockchain-api-types": "6.1.1",
"@stacks/transactions": "6.9.0",
Expand All @@ -26,6 +27,7 @@
"axios": "^1.1.3",
"bignumber.js": "^9.1.0",
"bip39": "^3.0.3",
"buffer": "6.0.3",
"c32check": "^2.0.0",
"classnames": "^2.3.2",
"crypto-browserify": "^3.12.0",
Expand Down Expand Up @@ -76,8 +78,7 @@
"style": "prettier --write \"src/**/*.{ts,tsx}\"",
"prepare": "husky install"
},
"resolutions": {
"styled-components": "^5",
"overrides": {
"buffer": "6.0.3"
},
"lint-staged": {
Expand Down Expand Up @@ -138,7 +139,8 @@
"tsc-files": "^1.1.4",
"tsconfig-paths-webpack-plugin": "^4.0.0",
"type-fest": "^2.19.0",
"typescript": "^4.8.2",
"typescript": "^5.0.0",
"typescript-plugin-styled-components": "^3.0.0",
"vitest": "^0.34.6",
"webpack": "^5.89.0",
"webpack-cli": "^4.0.0",
Expand Down
34 changes: 29 additions & 5 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import LoadingScreen from '@components/loadingScreen';
import { CheckCircle } from '@phosphor-icons/react';
import { CheckCircle, XCircle } from '@phosphor-icons/react';
import rootStore from '@stores/index';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
Expand All @@ -11,13 +11,20 @@ import { Toaster } from 'react-hot-toast';
import { Provider } from 'react-redux';
import { RouterProvider } from 'react-router-dom';
import { PersistGate } from 'redux-persist/integration/react';
import { ThemeProvider } from 'styled-components';
import styled, { ThemeProvider } from 'styled-components';
import '../locales';
import Theme from '../theme';
import GlobalStyle from '../theme/global';
import SessionGuard from './components/guards/session';
import router from './routes';

// needed to keep the svg icon scale for toasts over multiple lines
const StyledIcon = styled.div`
display: flex;
align-items: center;
justify-content: center;
`;

function App(): JSX.Element {
useEffect(() => {
if (!MIX_PANEL_TOKEN) {
Expand Down Expand Up @@ -46,16 +53,33 @@ function App(): JSX.Element {
containerStyle={{ bottom: 80 }}
toastOptions={{
success: {
icon: <CheckCircle size={20} weight="bold" />,
icon: (
<StyledIcon>
<CheckCircle size={20} weight="bold" />
</StyledIcon>
),
style: {
...Theme.typography.body_medium_m,
backgroundColor: Theme.colors.success_medium,
borderRadius: Theme.radius(2),
padding: Theme.spacing(4),
paddingLeft: Theme.spacing(6),
padding: Theme.space.s,
color: Theme.colors.elevation0,
},
},
error: {
icon: (
<StyledIcon>
<XCircle size={20} weight="bold" />
</StyledIcon>
),
style: {
...Theme.typography.body_medium_m,
backgroundColor: Theme.colors.danger_dark,
borderRadius: Theme.radius(2),
padding: Theme.space.s,
color: Theme.colors.white_0,
},
},
}}
/>
</ThemeProvider>
Expand Down
10 changes: 5 additions & 5 deletions src/app/components/bottomModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Cross from '@assets/img/dashboard/X.svg';
import Separator from '@components/separator';
import { XCircle } from '@phosphor-icons/react';
import Modal from 'react-modal';
import styled, { useTheme } from 'styled-components';

Expand All @@ -11,9 +11,9 @@ const BottomModalHeaderText = styled.h1((props) => ({
const RowContainer = styled.div((props) => ({
display: 'flex',
flexDirection: 'row',
alignItems: 'space-between',
margin: props.theme.spacing(12),
marginBottom: props.theme.spacing(10),
alignItems: 'center',
justifyContent: 'space-between',
margin: props.theme.space.m,
}));

const ButtonImage = styled.button({
Expand Down Expand Up @@ -87,7 +87,7 @@ function BottomModal({
<RowContainer>
<BottomModalHeaderText>{header}</BottomModalHeaderText>
<ButtonImage onClick={onClose}>
<img src={Cross} alt="cross" />
<XCircle color={theme.colors.white_200} weight="fill" size="28" />
</ButtonImage>
</RowContainer>
{header && <Separator />}
Expand Down
229 changes: 229 additions & 0 deletions src/app/components/confirmBtcTransaction/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import ledgerConnectDefaultIcon from '@assets/img/ledger/ledger_connect_default.svg';
import ledgerConnectBtcIcon from '@assets/img/ledger/ledger_import_connect_btc.svg';
import { delay } from '@common/utils/ledger';
import BottomModal from '@components/bottomModal';
import ActionButton from '@components/button';
import LedgerConnectionView from '@components/ledger/connectLedgerView';
import useWalletSelector from '@hooks/useWalletSelector';
import TransportFactory from '@ledgerhq/hw-transport-webusb';
import { btcTransaction, Transport } from '@secretkeylabs/xverse-core';
import Callout from '@ui-library/callout';
import { StickyHorizontalSplitButtonContainer, StyledP } from '@ui-library/common.styled';
import { isLedgerAccount } from '@utils/helper';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { MoonLoader } from 'react-spinners';
import styled from 'styled-components';
import SendLayout from '../../layouts/sendLayout';
import TransactionSummary from './transactionSummary';

const LoaderContainer = styled.div(() => ({
display: 'flex',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}));

const ReviewTransactionText = styled(StyledP)`
text-align: left;
margin-bottom: ${(props) => props.theme.space.l};
`;

const BroadcastCallout = styled(Callout)`
margin-bottom: ${(props) => props.theme.space.m};
`;

const SuccessActionsContainer = styled.div((props) => ({
width: '100%',
display: 'flex',
flexDirection: 'column',
gap: props.theme.space.s,
paddingLeft: props.theme.space.m,
paddingRight: props.theme.space.m,
marginBottom: props.theme.space.xxl,
marginTop: props.theme.space.xxl,
}));

type Props = {
inputs: btcTransaction.EnhancedInput[];
outputs: btcTransaction.EnhancedOutput[];
feeOutput?: btcTransaction.TransactionFeeOutput;
isLoading: boolean;
isSubmitting: boolean;
isBroadcast?: boolean;
isError?: boolean;
showAccountHeader?: boolean;
hideBottomBar?: boolean;
cancelText: string;
confirmText: string;
onConfirm: (ledgerTransport?: Transport) => void;
onCancel: () => void;
onBackClick?: () => void;
confirmDisabled?: boolean;
getFeeForFeeRate?: (feeRate: number, useEffectiveFeeRate?: boolean) => Promise<number>;
onFeeRateSet?: (feeRate: number) => void;
};

function ConfirmBtcTransaction({
inputs,
outputs,
feeOutput,
isLoading,
isSubmitting,
isBroadcast,
isError = false,
cancelText,
confirmText,
onConfirm,
onCancel,
onBackClick,
showAccountHeader,
hideBottomBar,
confirmDisabled = false,
getFeeForFeeRate,
onFeeRateSet,
}: Props) {
const [isModalVisible, setIsModalVisible] = useState(false);
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
const [isConnectSuccess, setIsConnectSuccess] = useState(false);
const [isConnectFailed, setIsConnectFailed] = useState(false);
const [isTxRejected, setIsTxRejected] = useState(false);

const { t } = useTranslation('translation', { keyPrefix: 'CONFIRM_TRANSACTION' });
const { t: signatureRequestTranslate } = useTranslation('translation', {
keyPrefix: 'SIGNATURE_REQUEST',
});
const { selectedAccount } = useWalletSelector();

const hideBackButton = !onBackClick;

const onConfirmPress = async () => {
if (!isLedgerAccount(selectedAccount)) {
return onConfirm();
}

// show ledger connection screens
setIsModalVisible(true);
};

const handleConnectAndConfirm = async () => {
if (!selectedAccount) {
console.error('No account selected');
return;
}
setIsButtonDisabled(true);

const transport = await TransportFactory.create();

if (!transport) {
setIsConnectSuccess(false);
setIsConnectFailed(true);
setIsButtonDisabled(false);
return;
}

setIsConnectSuccess(true);
await delay(1500);
setCurrentStepIndex(1);

try {
onConfirm(transport);
} catch (err) {
console.error(err);
setIsTxRejected(true);
}
};

const handleRetry = async () => {
setIsTxRejected(false);
setIsConnectSuccess(false);
setCurrentStepIndex(0);
};

// TODO: this is a bit naive, but should be correct. We may want to look at the sig hash types of the inputs instead
const isPartialTransaction = !feeOutput;

return isLoading ? (
<LoaderContainer>
<MoonLoader color="white" size={50} />
</LoaderContainer>
) : (
<>
<SendLayout
selectedBottomTab="dashboard"
onClickBack={onBackClick}
hideBackButton={hideBackButton}
showAccountHeader={showAccountHeader}
hideBottomBar={hideBottomBar}
>
<ReviewTransactionText typography="headline_s">
{t('REVIEW_TRANSACTION')}
</ReviewTransactionText>
{!isBroadcast && <BroadcastCallout bodyText={t('PSBT_NO_BROADCAST_DISCLAIMER')} />}
<TransactionSummary
inputs={inputs}
outputs={outputs}
feeOutput={feeOutput}
isPartialTransaction={isPartialTransaction}
getFeeForFeeRate={getFeeForFeeRate}
onFeeRateSet={onFeeRateSet}
isSubmitting={isSubmitting}
/>
{!isLoading && (
<StickyHorizontalSplitButtonContainer>
<ActionButton onPress={onCancel} text={cancelText} transparent />
<ActionButton
onPress={onConfirmPress}
disabled={confirmDisabled}
processing={isSubmitting}
text={confirmText}
warning={isError}
/>
</StickyHorizontalSplitButtonContainer>
)}
</SendLayout>
<BottomModal header="" visible={isModalVisible} onClose={() => setIsModalVisible(false)}>
{currentStepIndex === 0 && (
<LedgerConnectionView
title={signatureRequestTranslate('LEDGER.CONNECT.TITLE')}
text={signatureRequestTranslate('LEDGER.CONNECT.SUBTITLE', { name: 'Bitcoin' })}
titleFailed={signatureRequestTranslate('LEDGER.CONNECT.ERROR_TITLE')}
textFailed={signatureRequestTranslate('LEDGER.CONNECT.ERROR_SUBTITLE')}
imageDefault={ledgerConnectBtcIcon}
isConnectSuccess={isConnectSuccess}
isConnectFailed={isConnectFailed}
/>
)}
{currentStepIndex === 1 && (
<LedgerConnectionView
title={signatureRequestTranslate('LEDGER.CONFIRM.TITLE')}
text={signatureRequestTranslate('LEDGER.CONFIRM.SUBTITLE')}
titleFailed={signatureRequestTranslate('LEDGER.CONFIRM.ERROR_TITLE')}
textFailed={signatureRequestTranslate('LEDGER.CONFIRM.ERROR_SUBTITLE')}
imageDefault={ledgerConnectDefaultIcon}
isConnectSuccess={false}
isConnectFailed={isTxRejected}
/>
)}
<SuccessActionsContainer>
<ActionButton
onPress={isTxRejected || isConnectFailed ? handleRetry : handleConnectAndConfirm}
text={signatureRequestTranslate(
isTxRejected || isConnectFailed ? 'LEDGER.RETRY_BUTTON' : 'LEDGER.CONNECT_BUTTON',
)}
disabled={isButtonDisabled}
processing={isButtonDisabled}
/>
<ActionButton
onPress={onCancel}
text={signatureRequestTranslate('LEDGER.CANCEL_BUTTON')}
transparent
/>
</SuccessActionsContainer>
</BottomModal>
</>
);
}

export default ConfirmBtcTransaction;
Loading

0 comments on commit ab80d89

Please sign in to comment.