Skip to content

Commit

Permalink
feat: add address store
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranjamie committed Jul 6, 2020
1 parent 9e149ec commit ace8647
Show file tree
Hide file tree
Showing 13 changed files with 1,083 additions and 579 deletions.
29 changes: 29 additions & 0 deletions app/api/get-account-details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import axios from 'axios';
import { TransactionResults } from '@blockstack/stacks-blockchain-sidecar-types';

const api = 'https://sidecar.staging.blockstack.xyz/sidecar';

//
// TODO: move to sidecar repo, should be documented there
export interface AddressBalanceResponse {
stx: {
balance: string;
total_sent: string;
total_received: string;
};
fungible_tokens: any;
non_fungible_tokens: any;
}

async function getAddressBalance(address: string) {
return await axios.get<AddressBalanceResponse>(api + `/v1/address/${address}/balances`);
}

async function getAddressTransactions(address: string) {
return await axios.get<TransactionResults>(api + `/v1/address/${address}/transactions`);
}

export const Api = {
getAddressBalance,
getAddressTransactions,
};
12 changes: 0 additions & 12 deletions app/api/get-balance.ts

This file was deleted.

2 changes: 1 addition & 1 deletion app/components/balance-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const BalanceCard: FC<BalanceCardProps> = ({ balance }) => {
Total balance
</Text>
<Text fontSize="40px" lineHeight="56px" fontWeight="bold" letterSpacing="-0.01em">
{balance}
{balance} STX
</Text>
<Box mt="loose">
<Button size="md">Send</Button>
Expand Down
46 changes: 22 additions & 24 deletions app/pages/home/home-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { FC } from 'react';
import { Flex, Box } from '@blockstack/ui';

type HomeComponents =
Expand All @@ -7,35 +7,33 @@ type HomeComponents =
| 'stackingPromoCard'
| 'stackingRewardCard';

type HomeLayout = {
type HomeLayoutProps = {
[key in HomeComponents]: JSX.Element;
};

export const HomeLayout: React.FC<HomeLayout> = ({
export const HomeLayout: FC<HomeLayoutProps> = ({
balanceCard,
transactionList,
stackingPromoCard,
stackingRewardCard,
}) => {
return (
<Flex
flexShrink={1}
maxWidth="1104px"
pt="120px"
flexDirection="column"
mx={['loose', 'loose', 'extra-loose', 'extra-loose', 'auto']}
mb="extra-loose"
>
{balanceCard}
<Flex flexDirection={['column', 'column', 'row']}>
<Box mt="extra-loose" flexGrow={1} mr={[null, null, 'extra-loose', '72px']}>
{transactionList}
</Box>
<Flex flexDirection="column" minWidth={[null, null, '376px']}>
{stackingPromoCard}
{stackingRewardCard}
</Flex>
}) => (
<Flex
flexShrink={1}
maxWidth="1104px"
pt="120px"
flexDirection="column"
mx={['loose', 'loose', 'extra-loose', 'extra-loose', 'auto']}
mb="extra-loose"
>
{balanceCard}
<Flex flexDirection={['column', 'column', 'row']}>
<Box mt="extra-loose" flexGrow={1} mr={[null, null, 'extra-loose', '72px']}>
{transactionList}
</Box>
<Flex flexDirection="column" minWidth={[null, null, '376px']}>
{stackingPromoCard}
{stackingRewardCard}
</Flex>
</Flex>
);
};
</Flex>
);
47 changes: 19 additions & 28 deletions app/pages/home/home.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,39 @@
import React, { useEffect, useState, FC } from 'react';
import React, { useEffect, FC } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { shell } from 'electron';
import { deriveRootKeychainFromMnemonic } from '@blockstack/keychain';

import { selectMnemonic } from '../../store/keys';
import { BIP32Interface } from '../../types/bip32';
import { TransactionList } from '../../components/transaction-list/transaction-list';
import { StackingPromoCard } from '../../components/stacking-promo-card';
import { StackingRewardCard } from '../../components/stacking-rewards-card';
import { BalanceCard } from '../../components/balance-card';

import { HomeLayout } from './home-layout';
import { selectAddress } from '../../store/keys/keys.reducer';
import { selectTransactions } from '../../store/transaction/transaction.reducer';
import { RootState } from '../../store/index';
import { getTransactions } from '../../store/transaction/transaction.actions';
import { getAddressTransactions } from '../../store/transaction/transaction.actions';
import { TransactionListItem } from '../../components/transaction-list/transaction-list-item';
import { getAddressDetails } from '../../store/address/address.actions';
import { HomeLayout } from './home-layout';
import { selectAddressBalance } from '../../store/address/address.reducer';
import { Spinner } from '@blockstack/ui';

export const Home: FC = () => {
const dispatch = useDispatch();
const { mnemonic, address, transactions } = useSelector((state: RootState) => ({
mnemonic: selectMnemonic(state),
const { address, balance, transactions } = useSelector((state: RootState) => ({
address: selectAddress(state),
transactions: selectTransactions(state),
balance: selectAddressBalance(state),
}));
const [keychain, setKeychain] = useState<{ rootNode: BIP32Interface } | null>(null);

useEffect(() => {
const deriveMasterKeychain = async () => {
if (!mnemonic) return;
const { rootNode } = await deriveRootKeychainFromMnemonic(mnemonic, '');
setKeychain({ rootNode });
};
void deriveMasterKeychain();
}, [mnemonic]);

// ST2K2GK11JDC2DT8M9B5T28P2FHMXMECKDGDNCQCN
useEffect(() => {
if (!address) return;
// Demo address
// ST2K2GK11JDC2DT8M9B5T28P2FHMXMECKDGDNCQCN
dispatch(getTransactions(address));
}, [dispatch, address]);

if (keychain === null || address === undefined) return <>loading</>;
if (address === undefined) return <Spinner />;

const openInExplorer = (txId: string) =>
shell.openExternal(`https://testnet-explorer.blockstack.org/txid/${txId}`);
Expand All @@ -54,18 +45,18 @@ export const Home: FC = () => {
))}
</TransactionList>
);
const balanceCard = <BalanceCard balance="124,000.1003 STX" />;
const balanceCard = <BalanceCard balance={balance === null ? '–' : balance} />;
const stackingPromoCard = <StackingPromoCard />;
const stackingRewardCard = (
<StackingRewardCard lifetime="0.0281 Bitcoin" lastCycle="0.000383 Bitcoin" />
);

const homeLayoutComponents = {
transactionList,
balanceCard,
stackingPromoCard,
stackingRewardCard,
};

return <HomeLayout {...homeLayoutComponents} />;
return (
<HomeLayout
transactionList={transactionList}
balanceCard={balanceCard}
stackingPromoCard={stackingPromoCard}
stackingRewardCard={stackingRewardCard}
/>
);
};
24 changes: 24 additions & 0 deletions app/store/address/address.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Dispatch } from '../index';
import { createAction } from '@reduxjs/toolkit';
import { safeAwait } from '@blockstack/ui';

import { Api, AddressBalanceResponse } from '../../api/get-account-details';

export const fetchAddress = createAction('address/fetch-address');
export const fetchAddressDone = createAction<AddressBalanceResponse>('address/fetch-address-done');
export const fetchAddressFail = createAction('address/fetch-address-fail');

export function getAddressDetails(address: string) {
return async (dispatch: Dispatch) => {
dispatch(fetchAddress());
const [error, response] = await safeAwait(Api.getAddressBalance(address));
if (error) {
dispatch(fetchAddressFail());
return;
}
if (response) {
const address = response.data;
dispatch(fetchAddressDone(address));
}
};
}
22 changes: 22 additions & 0 deletions app/store/address/address.reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createReducer, createSelector } from '@reduxjs/toolkit';

import { RootState } from '..';
import { fetchAddressDone } from './address.actions';

export interface AddressState {
balance: string | null;
}

const initialState: AddressState = {
balance: null,
};

export const addressReducer = createReducer(initialState, builder =>
builder.addCase(fetchAddressDone, (_state, { payload }) => ({
balance: payload.stx.balance,
}))
);

export const selectAddressState = (state: RootState) => state.address;

export const selectAddressBalance = createSelector(selectAddressState, state => state.balance);
1 change: 1 addition & 0 deletions app/store/address/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './address.reducer';
3 changes: 3 additions & 0 deletions app/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { connectRouter } from 'connected-react-router';

import { KeysState, createKeysReducer } from './keys';
import { TransactionState, transactionReducer } from './transaction/transaction.reducer';
import { addressReducer, AddressState } from './address';

export interface RootState {
router: any;
keys: KeysState;
transaction: TransactionState;
address: AddressState;
}

export type GetState = () => RootState;
Expand All @@ -27,5 +29,6 @@ export function createRootReducer({ history, keys }: RootReducerArgs) {
router: connectRouter(history),
keys: createKeysReducer(keys),
transaction: transactionReducer,
address: addressReducer,
});
}
4 changes: 0 additions & 4 deletions app/store/keys/keys.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ export const persistMnemonicSafe = createAction<string>('keys/save-mnemonic-safe

export const persistMnemonic = createAction<string>('keys/save-mnemonic');

export const addData = createAction<any>('keys/add-all');

interface SetPasswordSuccess {
salt: string;
encryptedMnemonic: string;
Expand Down Expand Up @@ -103,8 +101,6 @@ export function decryptWallet({ password, history }: { password: string; history
const { rootNode } = await deriveRootKeychainFromMnemonic(mnemonic, '');
console.log({ rootNode });
const { address } = deriveStxAddressChain(ChainID.Mainnet)(rootNode);
// const pubkey = pubKeyfromPrivKey(rootNode.privateKey);
// console.log({ addressKeychain });
dispatch(attemptWalletDecryptSuccess({ salt, mnemonic, address }));
history.push(routes.HOME);
}
Expand Down
6 changes: 3 additions & 3 deletions app/store/transaction/transaction.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import { createAction } from '@reduxjs/toolkit';
import { safeAwait } from '@blockstack/ui';
import { Transaction } from '@blockstack/stacks-blockchain-sidecar-types';

import { getAddressTransactions } from '../../api/get-balance';
import { Api } from '../../api/get-account-details';

export const fetchTransactions = createAction('transactions/fetch-transactions');
export const fetchTransactionsDone = createAction<Transaction[]>(
'transactions/fetch-transactions-done'
);
export const fetchTransactionsFail = createAction('transactions/fetch-transactions-fail');

export function getTransactions(address: string) {
export function getAddressTransactions(address: string) {
return async (dispatch: Dispatch) => {
dispatch(fetchTransactions());
const [error, response] = await safeAwait(getAddressTransactions(address));
const [error, response] = await safeAwait(Api.getAddressTransactions(address));
if (error) {
dispatch(fetchTransactionsFail());
return;
Expand Down
Loading

0 comments on commit ace8647

Please sign in to comment.