chore: refactor fee and price context

This commit is contained in:
AP 2020-02-17 08:32:14 +01:00
parent 420cbbfa8f
commit c5debb9013
22 changed files with 287 additions and 122 deletions

View file

@ -1,6 +1,6 @@
{
"name": "app",
"version": "0.1.1",
"version": "0.1.2",
"private": true,
"dependencies": {
"@apollo/react-hooks": "^3.1.3",

View file

@ -2,27 +2,44 @@ import React from 'react';
import { useSpring, animated } from 'react-spring';
import { getValue } from '../../helpers/Helpers';
import { useSettings } from '../../context/SettingsContext';
import { usePriceState } from 'context/PriceContext';
interface AnimatedProps {
type PriceProps = {
price: number;
symbol: string;
currency: string;
};
type AnimatedProps = {
amount: number;
}
};
export const AnimatedNumber = ({ amount }: AnimatedProps) => {
const { value } = useSpring({
from: { value: 0 },
value: amount,
});
const { currency, prices } = useSettings();
const { currency } = useSettings();
const { prices } = usePriceState();
let priceProps: PriceProps = {
price: 0,
symbol: '',
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
};
if (prices) {
const current: { last: number; symbol: string } = prices[currency] ?? {
last: 0,
symbol: '',
};
const priceProps = {
priceProps = {
price: current.last,
symbol: current.symbol,
currency,
};
}
return (
<animated.div>

View file

@ -1,21 +1,25 @@
import { useEffect } from 'react';
import { useQuery } from '@apollo/react-hooks';
import { GET_BITCOIN_FEES } from '../../graphql/query';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../utils/error';
import { useBitcoinInfo } from '../../context/BitcoinContext';
import { useBitcoinDispatch } from '../../context/BitcoinContext';
export const BitcoinFees = () => {
const { setInfo } = useBitcoinInfo();
const { loading, data } = useQuery(GET_BITCOIN_FEES, {
onError: error => toast.error(getErrorContent(error)),
const setInfo = useBitcoinDispatch();
const { loading, data, stopPolling } = useQuery(GET_BITCOIN_FEES, {
onError: () => {
setInfo({ type: 'error' });
stopPolling();
},
pollInterval: 60000,
});
useEffect(() => {
if (!loading && data && data.getBitcoinFees) {
const { fast, halfHour, hour } = data.getBitcoinFees;
setInfo({ fast, halfHour, hour });
setInfo({
type: 'fetched',
state: { loading: false, error: false, fast, halfHour, hour },
});
}
}, [data, loading, setInfo]);

View file

@ -1,14 +1,12 @@
import { useEffect } from 'react';
import { useQuery } from '@apollo/react-hooks';
import { GET_BITCOIN_PRICE } from '../../graphql/query';
import { useSettings } from '../../context/SettingsContext';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../utils/error';
import { usePriceDispatch } from '../../context/PriceContext';
export const BitcoinPrice = () => {
const { setSettings } = useSettings();
const setPrices = usePriceDispatch();
const { loading, data, stopPolling } = useQuery(GET_BITCOIN_PRICE, {
onError: error => toast.error(getErrorContent(error)),
onError: () => setPrices({ type: 'error' }),
pollInterval: 60000,
});
@ -16,12 +14,16 @@ export const BitcoinPrice = () => {
if (!loading && data && data.getBitcoinPrice) {
try {
const prices = JSON.parse(data.getBitcoinPrice);
setSettings({ prices });
setPrices({
type: 'fetched',
state: { loading: false, error: false, prices },
});
} catch (error) {
stopPolling();
setPrices({ type: 'error' });
}
}
}, [data, loading, setSettings, stopPolling]);
}, [data, loading, setPrices, stopPolling]);
return null;
};

View file

@ -1,6 +1,13 @@
import React from 'react';
import { useSettings } from 'context/SettingsContext';
import { getValue } from 'helpers/Helpers';
import { usePriceState } from 'context/PriceContext';
type PriceProps = {
price: number;
symbol: string;
currency: string;
};
export const Price = ({
amount,
@ -9,17 +16,27 @@ export const Price = ({
amount: number;
breakNumber?: boolean;
}) => {
const { currency, prices } = useSettings();
const { currency } = useSettings();
const { prices, loading, error } = usePriceState();
let priceProps: PriceProps = {
price: 0,
symbol: '',
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
};
if (prices && !loading && !error) {
const current: { last: number; symbol: string } = prices[currency] ?? {
last: 0,
symbol: '',
};
const priceProps = {
priceProps = {
price: current.last,
symbol: current.symbol,
currency,
};
}
const getFormat = (amount: number) =>
getValue({ amount, ...priceProps, breakNumber });
@ -27,24 +44,40 @@ export const Price = ({
return <>{getFormat(amount)}</>;
};
export const getPrice = (context: any) => ({
export const getPrice = (
currency: string,
priceContext: {
error: boolean;
loading: boolean;
prices?: { [key: string]: { last: number; symbol: string } };
},
) => ({
amount,
breakNumber = false,
}: {
amount: number;
breakNumber?: boolean;
}) => {
const { currency, prices } = context;
const { prices, loading, error } = priceContext;
let priceProps: PriceProps = {
price: 0,
symbol: '',
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
};
if (prices && !loading && !error) {
const current: { last: number; symbol: string } = prices[currency] ?? {
last: 0,
symbol: '',
};
const priceProps = {
priceProps = {
price: current.last,
symbol: current.symbol,
currency,
};
}
const getFormat = (amount: number) =>
getValue({ amount, ...priceProps, breakNumber });

View file

@ -1,54 +1,76 @@
import React, { createContext, useState, useContext } from 'react';
import merge from 'lodash.merge';
import React, { createContext, useContext, useReducer } from 'react';
interface ChangeProps {
fast?: number;
halfHour?: number;
hour?: number;
}
interface BitcoinProps {
type State = {
loading: boolean;
error: boolean;
fast: number;
halfHour: number;
hour: number;
setInfo: (newProps: ChangeProps) => void;
}
};
export const BitcoinInfoContext = createContext<BitcoinProps>({
type ActionType = {
type: 'fetched' | 'error';
state?: State;
};
type Dispatch = (action: ActionType) => void;
export const StateContext = createContext<State | undefined>(undefined);
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
const initialState = {
loading: true,
error: false,
fast: 0,
halfHour: 0,
hour: 0,
setInfo: () => {},
});
};
const stateReducer = (state: State, action: ActionType): State => {
switch (action.type) {
case 'fetched':
return action.state || initialState;
case 'error':
return {
...initialState,
loading: false,
error: true,
};
default:
return initialState;
}
};
const BitcoinInfoProvider = ({ children }: any) => {
const setInfo = ({ fast, halfHour, hour }: ChangeProps) => {
updateSettings((prevState: any) => {
const newState = { ...prevState };
return merge(newState, {
fast,
halfHour,
hour,
});
});
};
const bitcoinInfoState = {
fast: 0,
halfHour: 0,
hour: 0,
setInfo,
};
const [settings, updateSettings] = useState(bitcoinInfoState);
const [state, dispatch] = useReducer(stateReducer, initialState);
return (
<BitcoinInfoContext.Provider value={settings}>
<DispatchContext.Provider value={dispatch}>
<StateContext.Provider value={state}>
{children}
</BitcoinInfoContext.Provider>
</StateContext.Provider>
</DispatchContext.Provider>
);
};
const useBitcoinInfo = () => useContext(BitcoinInfoContext);
const useBitcoinState = () => {
const context = useContext(StateContext);
if (context === undefined) {
throw new Error(
'useBitcoinState must be used within a BitcoinInfoProvider',
);
}
return context;
};
export { BitcoinInfoProvider, useBitcoinInfo };
const useBitcoinDispatch = () => {
const context = useContext(DispatchContext);
if (context === undefined) {
throw new Error(
'useBitcoinDispatch must be used within a BitcoinInfoProvider',
);
}
return context;
};
export { BitcoinInfoProvider, useBitcoinState, useBitcoinDispatch };

View file

@ -4,14 +4,17 @@ import { SettingsProvider } from './SettingsContext';
import { BitcoinInfoProvider } from './BitcoinContext';
import { ConnectionProvider } from './ConnectionContext';
import { StatusProvider } from './StatusContext';
import { PriceProvider } from './PriceContext';
export const ContextProvider: React.FC = ({ children }) => (
<AccountProvider>
<SettingsProvider>
<BitcoinInfoProvider>
<PriceProvider>
<ConnectionProvider>
<StatusProvider>{children}</StatusProvider>
</ConnectionProvider>
</PriceProvider>
</BitcoinInfoProvider>
</SettingsProvider>
</AccountProvider>

View file

@ -0,0 +1,73 @@
import React, { createContext, useContext, useReducer } from 'react';
type PriceProps = {
last: number;
symbol: string;
};
type State = {
loading: boolean;
error: boolean;
prices?: { [key: string]: PriceProps };
};
type ActionType = {
type: 'fetched' | 'error';
state?: State;
};
type Dispatch = (action: ActionType) => void;
export const StateContext = createContext<State | undefined>(undefined);
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
const initialState: State = {
loading: true,
error: false,
prices: { EUR: { last: 0, symbol: '€' } },
};
const stateReducer = (state: State, action: ActionType): State => {
switch (action.type) {
case 'fetched':
return action.state || initialState;
case 'error':
return {
...initialState,
loading: false,
error: true,
};
default:
return initialState;
}
};
const PriceProvider = ({ children }: any) => {
const [state, dispatch] = useReducer(stateReducer, initialState);
return (
<DispatchContext.Provider value={dispatch}>
<StateContext.Provider value={state}>
{children}
</StateContext.Provider>
</DispatchContext.Provider>
);
};
const usePriceState = () => {
const context = useContext(StateContext);
if (context === undefined) {
throw new Error('usePriceState must be used within a PriceProvider');
}
return context;
};
const usePriceDispatch = () => {
const context = useContext(DispatchContext);
if (context === undefined) {
throw new Error('usePriceDispatch must be used within a PriceProvider');
}
return context;
};
export { PriceProvider, usePriceState, usePriceDispatch };

View file

@ -1,20 +1,13 @@
import React, { createContext, useState, useContext } from 'react';
import merge from 'lodash.merge';
interface PriceProps {
last: number;
symbol: string;
}
interface ChangeProps {
prices?: { [key: string]: PriceProps };
theme?: string;
sidebar?: boolean;
currency?: string;
}
interface SettingsProps {
prices: { [key: string]: PriceProps };
currency: string;
theme: string;
sidebar: boolean;
@ -22,7 +15,6 @@ interface SettingsProps {
}
export const SettingsContext = createContext<SettingsProps>({
prices: {},
currency: '',
theme: '',
sidebar: true,
@ -35,11 +27,10 @@ const SettingsProvider = ({ children }: any) => {
localStorage.getItem('sidebar') === 'false' ? false : true;
const savedCurrency = localStorage.getItem('currency') || 'sat';
const setSettings = ({ prices, currency, theme, sidebar }: ChangeProps) => {
const setSettings = ({ currency, theme, sidebar }: ChangeProps) => {
updateSettings((prevState: any) => {
const newState = { ...prevState };
return merge(newState, {
prices,
currency,
theme,
sidebar,

View file

@ -45,11 +45,11 @@ const StatusProvider = ({ children }: any) => {
const [state, dispatch] = useReducer(stateReducer, initialState);
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
<StateContext.Provider value={state}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
</DispatchContext.Provider>
);
};

View file

@ -26,6 +26,7 @@ import ScaleLoader from 'react-spinners/ScaleLoader';
import { getPrice } from 'components/price/Price';
import { AnimatedNumber } from 'components/animated/AnimatedNumber';
import { useStatusState } from 'context/StatusContext';
import { usePriceState } from 'context/PriceContext';
const Closed = styled.div`
display: flex;
@ -90,8 +91,9 @@ export const NodeInfo = ({ isOpen, isBurger }: NodeInfoProps) => {
onError: error => toast.error(getErrorContent(error)),
});
const { theme, ...context } = useSettings();
const format = getPrice(context);
const { theme, currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
const tooltipType = getTooltipType(theme);

View file

@ -15,6 +15,7 @@ import {
import styled from 'styled-components';
import { getPrice } from 'components/price/Price';
import { useSettings } from 'context/SettingsContext';
import { usePriceState } from 'context/PriceContext';
export const AddMargin = styled.div`
margin-right: 10px;
@ -33,8 +34,9 @@ export const TransactionsCard = ({
setIndexOpen,
indexOpen,
}: TransactionsCardProps) => {
const context = useSettings();
const format = getPrice(context);
const { currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
const {
block_id,

View file

@ -8,7 +8,6 @@ import {
export const Progress = styled.div`
margin: 5px;
padding: 2px;
background: ${progressBackground};
`;

View file

@ -36,6 +36,7 @@ import { AdminSwitch } from '../../../components/adminSwitch/AdminSwitch';
import { DownArrow, UpArrow, EyeOff } from '../../../components/generic/Icons';
import { ColorButton } from '../../../components/buttons/colorButton/ColorButton';
import { getPrice } from 'components/price/Price';
import { usePriceState } from 'context/PriceContext';
const IconPadding = styled.div`
margin-left: 16px;
@ -65,8 +66,9 @@ export const ChannelCard = ({
}: ChannelCardProps) => {
const [modalOpen, setModalOpen] = useState(false);
const { theme, ...context } = useSettings();
const format = getPrice(context);
const { theme, currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
const tooltipType = getTooltipType(theme);

View file

@ -27,6 +27,7 @@ import {
} from '../../../components/generic/Helpers';
import { getNodeLink } from '../../../components/generic/Helpers';
import { getPrice } from 'components/price/Price';
import { usePriceState } from 'context/PriceContext';
interface PendingCardProps {
channelInfo: any;
@ -41,8 +42,9 @@ export const PendingCard = ({
setIndexOpen,
indexOpen,
}: PendingCardProps) => {
const { theme, ...context } = useSettings();
const format = getPrice(context);
const { theme, currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
const tooltipType = getTooltipType(theme);

View file

@ -12,7 +12,7 @@ import { PAY_ADDRESS } from '../../../../graphql/mutation';
import styled from 'styled-components';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../../../utils/error';
import { useBitcoinInfo } from '../../../../context/BitcoinContext';
import { useBitcoinState } from '../../../../context/BitcoinContext';
import { SecureButton } from '../../../../components/buttons/secureButton/SecureButton';
import { Input } from '../../../../components/input/Input';
import { useSize } from '../../../../hooks/UseSize';
@ -26,6 +26,7 @@ import { useSettings } from 'context/SettingsContext';
import Modal from 'components/modal/ReactModal';
import { ColorButton } from 'components/buttons/colorButton/ColorButton';
import { renderLine } from 'components/generic/Helpers';
import { usePriceState } from 'context/PriceContext';
const ResponsiveWrap = styled(SingleLine)`
@media (${mediaWidths.mobile}) {
@ -44,8 +45,9 @@ const Margin = styled.div`
`;
export const SendOnChainCard = ({ setOpen }: { setOpen: () => void }) => {
const context = useSettings();
const format = getPrice(context);
const { currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
const [modalOpen, setModalOpen] = useState(false);
@ -58,7 +60,7 @@ export const SendOnChainCard = ({ setOpen }: { setOpen: () => void }) => {
const canSend = address !== '' && (sendAll || tokens > 0) && amount > 0;
const { fast, halfHour, hour } = useBitcoinInfo();
const { fast, halfHour, hour } = useBitcoinState();
const [payAddress, { loading }] = useMutation(PAY_ADDRESS, {
onError: error => toast.error(getErrorContent(error)),

View file

@ -12,7 +12,7 @@ import { ChevronRight } from '../../../../components/generic/Icons';
import { OPEN_CHANNEL } from '../../../../graphql/mutation';
import { getErrorContent } from '../../../../utils/error';
import { toast } from 'react-toastify';
import { useBitcoinInfo } from '../../../../context/BitcoinContext';
import { useBitcoinState } from '../../../../context/BitcoinContext';
import styled from 'styled-components';
import { SecureButton } from '../../../../components/buttons/secureButton/SecureButton';
import { Input } from '../../../../components/input/Input';
@ -43,7 +43,7 @@ export const OpenChannelCard = ({ color, setOpenCard }: OpenChannelProps) => {
const [privateChannel, setPrivateChannel] = useState(false);
const [type, setType] = useState('none');
const { fast, halfHour, hour } = useBitcoinInfo();
const { fast, halfHour, hour } = useBitcoinState();
const [openChannel] = useMutation(OPEN_CHANNEL, {
onError: error => toast.error(getErrorContent(error)),

View file

@ -5,6 +5,7 @@ import { VictoryPie } from 'victory';
import { chartAxisColor } from '../../../../styles/Themes';
import { Row, Col, PieRow } from '../flow';
import { getPrice } from 'components/price/Price';
import { usePriceState } from 'context/PriceContext';
interface Props {
flowPie: { x: string; y: number }[];
@ -12,8 +13,9 @@ interface Props {
}
export const FlowPie = ({ flowPie, isType }: Props) => {
const { theme, ...context } = useSettings();
const format = getPrice(context);
const { theme, currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
return (
<Row>

View file

@ -16,6 +16,7 @@ import {
flowBarColor2,
} from '../../../../styles/Themes';
import { getPrice } from 'components/price/Price';
import { usePriceState } from 'context/PriceContext';
// import { WaterfallProps } from '.';
// const beforeMap = {
@ -40,8 +41,9 @@ export const FlowReport = ({
parsedData2,
}: // waterfall,
Props) => {
const { theme, ...context } = useSettings();
const format = getPrice(context);
const { theme, currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
let domain = 24;
let barWidth = 3;

View file

@ -20,6 +20,7 @@ import styled from 'styled-components';
import { LoadingCard } from '../../../../components/loading/LoadingCard';
import { getPrice } from 'components/price/Price';
import { useSettings } from 'context/SettingsContext';
import { usePriceState } from 'context/PriceContext';
const ChannelRow = styled.div`
font-size: 14px;
@ -68,8 +69,10 @@ interface Props {
export const ForwardChannelsReport = ({ isTime, isType, color }: Props) => {
const [type, setType] = useState('route');
const context = useSettings();
const format = getPrice(context);
const { currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
const { host, read, cert, sessionAdmin } = useAccount();
const auth = getAuthString(host, read !== '' ? read : sessionAdmin, cert);

View file

@ -22,6 +22,7 @@ import { toast } from 'react-toastify';
import { getErrorContent } from '../../../../utils/error';
import { LoadingCard } from '../../../../components/loading/LoadingCard';
import { getPrice } from 'components/price/Price';
import { usePriceState } from 'context/PriceContext';
interface Props {
isTime: string;
@ -35,8 +36,9 @@ const timeMap: { [key: string]: string } = {
};
export const ForwardReport = ({ isTime, isType }: Props) => {
const { theme, ...context } = useSettings();
const format = getPrice(context);
const { theme, currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
const { host, read, cert, sessionAdmin } = useAccount();
const auth = getAuthString(host, read !== '' ? read : sessionAdmin, cert);

View file

@ -25,14 +25,16 @@ import {
import { LoadingCard } from '../../../../components/loading/LoadingCard';
import { getPrice } from 'components/price/Price';
import { useSize } from 'hooks/UseSize';
import { usePriceState } from 'context/PriceContext';
export const LiquidReport = () => {
const { width } = useSize();
const { host, read, cert, sessionAdmin } = useAccount();
const auth = getAuthString(host, read !== '' ? read : sessionAdmin, cert);
const { theme, ...context } = useSettings();
const format = getPrice(context);
const { theme, currency } = useSettings();
const priceContext = usePriceState();
const format = getPrice(currency, priceContext);
const { data, loading } = useQuery(GET_LIQUID_REPORT, {
variables: { auth },