Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.

Commit

Permalink
feat: add back buttons to onboarding flow
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranjamie committed Sep 25, 2020
1 parent 7fa64e5 commit 6706959
Show file tree
Hide file tree
Showing 18 changed files with 157 additions and 86 deletions.
9 changes: 1 addition & 8 deletions app/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,9 @@ async function getAddressTransactions(address: string) {
return await axios.get<TransactionResults>(api + `/v1/address/${address}/transactions`);
}

type AnyTransaction = Transaction | MempoolTransaction;

async function getTxDetails(txid: string) {
return await axios.get<AnyTransaction>(api + `/v1/tx/${txid}/transactions`);
return await axios.get<Transaction | MempoolTransaction>(api + `/v1/tx/${txid}/transactions`);
}
// async function getStxPriceInDollars() {
// return await axios.get(
// 'https://api.coingecko.com/api/v3/simple/price?ids=blockstack&vs_currencies=usd'
// );
// }

export const Api = {
getAddressBalance,
Expand Down
3 changes: 2 additions & 1 deletion app/components/home/balance-card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { FC } from 'react';
import { Box, Button, Text, ArrowIcon } from '@blockstack/ui';
import { humanReadableStx } from '../../utils/format-stx';

interface BalanceCardProps {
balance: string | null;
Expand All @@ -14,7 +15,7 @@ export const BalanceCard: FC<BalanceCardProps> = ({ balance, onSelectReceive, on
Total balance
</Text>
<Text fontSize="40px" lineHeight="56px" fontWeight="bold" letterSpacing="-0.01em">
{balance === null ? '–' : balance} µSTX
{balance === null ? '–' : humanReadableStx(balance)}
</Text>
<Box mt="loose">
<Button size="md" onClick={onSelectSend} isDisabled={balance === '0' || balance === null}>
Expand Down
1 change: 1 addition & 0 deletions app/components/onboarding/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './onboarding';
export * from './onboarding-button';
export * from './onboarding-back-button';
export * from './onboarding-text';
export * from './onboarding-title';
export * from './onboarding-footer';
Expand Down
22 changes: 22 additions & 0 deletions app/components/onboarding/onboarding-back-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Button, ButtonProps, ArrowIcon } from '@blockstack/ui';

type OnboardingBackButton = Omit<ButtonProps, 'ref' | 'children'>;

export const OnboardingBackButton: React.FC<OnboardingBackButton> = props => (
<Button
variant="solid"
mode="tertiary"
width="32px"
height="32px"
type="button"
top="20px"
left="20px"
style={{
position: 'absolute',
}}
{...props}
>
<ArrowIcon direction="left" size="12px" />
</Button>
);
5 changes: 3 additions & 2 deletions app/crypto/create-stx-tx.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import BN from 'bn.js';
import BigNumber from 'bignumber.js';
import { deriveRootKeychainFromMnemonic } from '@blockstack/keychain';
import { makeSTXTokenTransfer } from '@blockstack/stacks-transactions';

Expand All @@ -8,15 +9,15 @@ import { deriveStxAddressKeychain } from './derive-address-keychain';
interface CreateStxTxArgs {
mnemonic: string;
recipient: string;
amount: BN;
amount: BigNumber;
}

export async function createStxTransaction({ mnemonic, recipient, amount }: CreateStxTxArgs) {
const rootNode = await deriveRootKeychainFromMnemonic(mnemonic);
const { privateKey } = deriveStxAddressKeychain(rootNode);
return await makeSTXTokenTransfer({
recipient,
amount,
amount: new BN(amount.toString()),
senderKey: privateKey,
network: stacksNetwork,
});
Expand Down
10 changes: 1 addition & 9 deletions app/menu.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
import {
app,
Menu,
shell,
BrowserWindow,
MenuItemConstructorOptions,
MenuItem,
remote,
} from 'electron';
import { app, Menu, shell, BrowserWindow, MenuItemConstructorOptions } from 'electron';

interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions {
selector?: string;
Expand Down
15 changes: 8 additions & 7 deletions app/modals/transaction/transaction-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { ErrorLabel } from '../../components/error-label';
import { ErrorText } from '../../components/error-text';
import { FormikProps } from 'formik';
import { capitalize } from '../../utils/capitalize';
import { humanReadableStx } from '../../utils/format-stx';

interface TxModalFormProps {
balance: string;
form: FormikProps<{ address: string; amount: string }>;
form: FormikProps<{ recipient: string; amount: string }>;
}

export const TxModalForm: FC<TxModalFormProps> = ({ balance, form }) => {
Expand All @@ -16,7 +17,7 @@ export const TxModalForm: FC<TxModalFormProps> = ({ balance, form }) => {
<Flex flexDirection="column" alignItems="center" mt="48px">
<Text textStyle="body.large.medium">Available balance</Text>
<Text textStyle="body.large.medium" fontWeight={600} mt="tight" fontSize="32px">
{balance}
{humanReadableStx(balance)}
</Text>
</Flex>
<Flex flexDirection="column" mt="40px" mx="extra-loose">
Expand All @@ -25,16 +26,16 @@ export const TxModalForm: FC<TxModalFormProps> = ({ balance, form }) => {
</Text>
<Input
id="stxAddress"
name="address"
name="recipient"
autoFocus
mt="base-tight"
placeholder="STX address"
onChange={form.handleChange}
value={form.values.address}
value={form.values.recipient}
/>
{form.touched.address && form.errors.address && (
{form.touched.recipient && form.errors.recipient && (
<ErrorLabel>
<ErrorText>{form.errors.address}</ErrorText>
<ErrorText>{form.errors.recipient}</ErrorText>
</ErrorLabel>
)}
<Text textStyle="body.small.medium" mt="base-loose" as="label">
Expand All @@ -46,7 +47,7 @@ export const TxModalForm: FC<TxModalFormProps> = ({ balance, form }) => {
inputMode="numeric"
pattern="[0-9]*"
mt="base-tight"
placeholder="0.00000 STX"
placeholder="0.000000 STX"
onChange={form.handleChange}
value={form.values.amount}
/>
Expand Down
77 changes: 34 additions & 43 deletions app/modals/transaction/transaction-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { useSelector, useDispatch } from 'react-redux';
import { useFormik } from 'formik';
import * as yup from 'yup';
import BN from 'bn.js';
import { Modal, Text, Button, safeAwait } from '@blockstack/ui';
import { broadcastTransaction, StacksTransaction } from '@blockstack/stacks-transactions';
import { Modal, Text, Button } from '@blockstack/ui';
import { StacksTransaction } from '@blockstack/stacks-transactions';

import { RootState, Dispatch } from '../../store';
import { RootState } from '../../store';
import { validateStacksAddress } from '../../utils/get-stx-transfer-direction';
// import { broadcastStxTransaction } from '../../store/transaction/transaction.actions';

import { selectTxModalOpen, homeActions } from '../../store/home/home.reducer';
import { TxModalForm } from './transaction-form';
import { selectMnemonic } from '../../store/keys';
Expand All @@ -20,9 +20,11 @@ import {
TxModalPreviewItem,
modalStyle,
} from './transaction-modal-layout';
import { stacksNetwork } from '../../crypto/environment';
import { createStxTransaction } from '../../crypto/create-stx-tx';
import { validateAddressChain } from '../../crypto/validate-address-net';
import { broadcastStxTransaction } from '../../store/transaction';
import { humanReadableStx, stxToMicroStx } from '../../utils/format-stx';
import { BigNumber } from 'bignumber.js';

interface TxModalProps {
balance: string;
Expand All @@ -42,6 +44,8 @@ export const TransactionModal: FC<TxModalProps> = ({ balance, address }) => {
const dispatch = useDispatch();
const [step, setStep] = useState(TxModalStep.DescribeTx);
const [fee, setFee] = useState(new BN(0));
const [amount, setAmount] = useState(new BigNumber(0));
const [total, setTotal] = useState(new BigNumber(0));
const [tx, setTx] = useState<null | StacksTransaction>(null);
const [loading, setLoading] = useState(false);
const { mnemonic, txModalOpen } = useSelector((state: RootState) => ({
Expand All @@ -51,11 +55,11 @@ export const TransactionModal: FC<TxModalProps> = ({ balance, address }) => {

const form = useFormik({
initialValues: {
address: '',
recipient: '',
amount: '',
},
validationSchema: yup.object().shape({
address: yup
recipient: yup
.string()
.test('test-is-stx-address', 'Must be a valid Stacks Address', (value = '') =>
validateStacksAddress(value)
Expand All @@ -73,19 +77,32 @@ export const TransactionModal: FC<TxModalProps> = ({ balance, address }) => {
.positive('You cannot send a negative amount of STX')
.typeError('Amount of STX must be described as number')
.min(1, 'Smallest transaction is 1 STX')
.test(
'test-has-less-than-or-equal-to-6-decimal-places',
'STX cannot have more than 6 decimal places',
(value: number) => {
const decimals = new BigNumber(value).toString().split('.')[1];
return decimals === undefined || decimals.length <= 6;
}
)
.required(),
}),
onSubmit: async () => {
if (!mnemonic) return;
setLoading(true);
const tx = await createStxTransaction({
mnemonic,
recipient: form.values.address,
amount: new BN(form.values.amount),
recipient: form.values.recipient,
amount: stxToMicroStx(form.values.amount),
});
// handle errors
const { amount, fee } = {
amount: stxToMicroStx(form.values.amount),
fee: tx.auth.spendingCondition?.fee as BN,
};
setTx(tx);
setFee(tx.auth.spendingCondition?.fee as BN);
setFee(fee);
setTotal(amount.plus(fee.toString()));
setAmount(amount);
setStep(TxModalStep.PreviewAndSend);
setLoading(false);
},
Expand All @@ -99,34 +116,6 @@ export const TransactionModal: FC<TxModalProps> = ({ balance, address }) => {
form.resetForm();
};

interface BroadcastStxTxArgs {
amount: BN;
recipient: string;
}

function broadcastStxTransaction({ tx }: { tx: StacksTransaction }) {
return async (dispatch: Dispatch, getState: () => RootState) => {
const [error, blockchainResponse] = await safeAwait(broadcastTransaction(tx, stacksNetwork));

if (error || !blockchainResponse) return null;
console.log({ error });
// anything but string of id === error
console.log(blockchainResponse);
if (typeof blockchainResponse !== 'string') {
// setError for ui
return;
}
// dispatch(
// addPendingTransaction({
// txId: pendingTxId as string,
// amount: amount.toString(),
// time: +new Date(),
// })
// );
return blockchainResponse;
};
}

const broadcastTx = () => {
if (tx === null) return;
dispatch(broadcastStxTransaction({ tx }));
Expand Down Expand Up @@ -157,12 +146,14 @@ export const TransactionModal: FC<TxModalProps> = ({ balance, address }) => {
body: (
<TxModalPreview>
<TxModalPreviewItem label="To">
<Text fontSize="13px">{form.values.address}</Text>
<Text fontSize="13px">{form.values.recipient}</Text>
</TxModalPreviewItem>
<TxModalPreviewItem label="Amount">
{humanReadableStx(amount.toString())}
</TxModalPreviewItem>
<TxModalPreviewItem label="Amount">{form.values.amount}</TxModalPreviewItem>
<TxModalPreviewItem label="Fee">{fee.toString()} µSTX</TxModalPreviewItem>
<TxModalPreviewItem label="Fee">{humanReadableStx(fee)}</TxModalPreviewItem>
<TxModalPreviewItem label="Total">
{new BN(form.values.amount).add(fee).toString()} µSTX
{humanReadableStx(total.toString())}
</TxModalPreviewItem>
</TxModalPreview>
),
Expand Down
2 changes: 0 additions & 2 deletions app/pages/home/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@ export const Home: FC = () => {
}));

const checkIfPendingTxIsComplete = async (address: string) => {
console.log({ pending: address });
const [error, txResponse] = await safeAwait(Api.getTxDetails(address));
if (error || !txResponse || txResponse.data.tx_status === 'pending') {
console.log('Error, it do not exist');
return;
}
if (txResponse.data.tx_status === 'success') {
Expand Down
2 changes: 2 additions & 0 deletions app/pages/onboarding/02-create-wallet/create-wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
OnboardingText,
OnboardingFooter,
OnboardingFooterLink,
OnboardingBackButton,
} from '../../../components/onboarding';

export const CreateWallet: React.FC = () => {
Expand All @@ -25,6 +26,7 @@ export const CreateWallet: React.FC = () => {
return (
<Onboarding>
<OnboardingTitle>Create a new wallet</OnboardingTitle>
<OnboardingBackButton onClick={() => history.push(routes.WELCOME)} />
<OnboardingText>
Please choose whether you’d like to connect a Ledger hardware wallet or to create a software
wallet
Expand Down
2 changes: 2 additions & 0 deletions app/pages/onboarding/03-restore-wallet/restore-wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
OnboardingText,
OnboardingFooter,
OnboardingFooterLink,
OnboardingBackButton,
} from '../../../components/onboarding';

export const RestoreWallet: React.FC = () => {
Expand Down Expand Up @@ -47,6 +48,7 @@ export const RestoreWallet: React.FC = () => {
return (
<Onboarding as="form" onSubmit={handleSecretKeyRestore}>
<OnboardingTitle>Restore your wallet</OnboardingTitle>
<OnboardingBackButton onClick={() => history.push(routes.WELCOME)} />
<OnboardingText>
Restore your wallet by connecting your Ledger hardware wallet or a by entering your Secret
Key
Expand Down
2 changes: 2 additions & 0 deletions app/pages/onboarding/05-secret-key/secret-key.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
OnboardingTitle,
OnboardingButton,
OnboardingText,
OnboardingBackButton,
} from '../../../components/onboarding';
import { selectMnemonic } from '../../../store/keys/keys.reducer';

Expand All @@ -39,6 +40,7 @@ export const SecretKey: React.FC = () => {
return (
<Onboarding>
<OnboardingTitle>Your Secret Key</OnboardingTitle>
<OnboardingBackButton onClick={() => history.push(routes.CREATE)} />
<OnboardingText>
Here’s your Secret Key: 24 words that prove it’s you when you want to access your wallet.
Once lost, it’s lost forever, so save it somewhere you won’t forget.
Expand Down
4 changes: 2 additions & 2 deletions app/pages/onboarding/06-save-key/save-key.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
OnboardingTitle,
OnboardingButton,
OnboardingText,
OnboardingBackButton,
} from '../../../components/onboarding';
import { Collapse, onboardingFaq } from '../../../components/secret-key-faq';

Expand All @@ -15,15 +16,14 @@ export const SaveKey: React.FC = () => {
return (
<Onboarding>
<OnboardingTitle>Save your Secret Key</OnboardingTitle>
<OnboardingBackButton onClick={() => history.push(routes.SECRET_KEY)} />
<OnboardingText>
Paste your Secret Key wherever you keep critical, private, information such as passwords.
Once lost, it’s lost forever. So save it somewhere you won’t forget.
</OnboardingText>

<OnboardingButton mt="extra-loose" onClick={() => history.push(routes.VERIFY_KEY)}>
I've saved it
</OnboardingButton>

<Collapse content={onboardingFaq('Stacks Wallet')} mt="extra-loose" />
</Onboarding>
);
Expand Down
2 changes: 2 additions & 0 deletions app/pages/onboarding/07-verify-key/verify-key.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
OnboardingButton,
OnboardingFooter,
OnboardingFooterLink,
OnboardingBackButton,
} from '../../../components/onboarding';

export const VerifyKey: React.FC = () => {
Expand All @@ -41,6 +42,7 @@ export const VerifyKey: React.FC = () => {
return (
<Onboarding as="form" onSubmit={handleMnemonicValidation}>
<OnboardingTitle>Verify Secret Key</OnboardingTitle>
<OnboardingBackButton onClick={() => history.push(routes.SECRET_KEY)} />
<OnboardingText>Enter your Secret Key to confirm you’ve saved it</OnboardingText>
<Input
as="textarea"
Expand Down
Loading

0 comments on commit 6706959

Please sign in to comment.