chore: 🔧 add details to channels

This commit is contained in:
Anthony Potdevin 2020-08-14 22:38:56 +02:00
parent 3c2e24f815
commit 5518c51866
No known key found for this signature in database
GPG key ID: 4403F1DFBE779457
7 changed files with 243 additions and 169 deletions

View file

@ -30,6 +30,7 @@ type BalanceProps = {
remote: number;
formatLocal?: string;
formatRemote?: string;
height?: number;
};
export const BalanceBars = ({
@ -37,6 +38,7 @@ export const BalanceBars = ({
remote,
formatLocal,
formatRemote,
height = 20,
}: BalanceProps) => {
const localOpposite = 100 - local;
const remoteOpposite = 100 - remote;
@ -57,10 +59,10 @@ export const BalanceBars = ({
<BalanceLine>
{hasLocal && <Value>{formatLocal}</Value>}
{hasRemote && <RightValue>{formatRemote}</RightValue>}
<ProgressBar barHeight={20} order={4} percent={localOpposite} />
<ProgressBar barHeight={20} order={1} percent={local} />
<ProgressBar barHeight={20} order={2} percent={remote} />
<ProgressBar barHeight={20} order={4} percent={remoteOpposite} />
<ProgressBar barHeight={height} order={4} percent={localOpposite} />
<ProgressBar barHeight={height} order={1} percent={local} />
<ProgressBar barHeight={height} order={2} percent={remote} />
<ProgressBar barHeight={height} order={4} percent={remoteOpposite} />
</BalanceLine>
</>
);

View file

@ -0,0 +1,142 @@
import React, { useState } from 'react';
import { toast } from 'react-toastify';
import { useUpdateFeesMutation } from 'src/graphql/mutations/__generated__/updateFees.generated';
import { getErrorContent } from 'src/utils/error';
import { InputWithDeco } from 'src/components/input/InputWithDeco';
import { useNodeInfo } from 'src/hooks/UseNodeInfo';
import { ColorButton } from 'src/components/buttons/colorButton/ColorButton';
type ChangeDetailsType = {
callback: () => void;
transaction_id?: string;
transaction_vout?: number;
base_fee_mtokens?: string | null;
max_htlc_mtokens?: string | null;
min_htlc_mtokens?: string | null;
fee_rate?: number | null;
cltv_delta?: number | null;
};
export const ChangeDetails = ({
callback,
transaction_id,
transaction_vout,
base_fee_mtokens,
max_htlc_mtokens,
min_htlc_mtokens,
fee_rate,
cltv_delta,
}: ChangeDetailsType) => {
const { minorVersion, revision } = useNodeInfo();
const canMax = (minorVersion === 7 && revision > 1) || minorVersion > 7;
const canMin = (minorVersion === 8 && revision > 2) || minorVersion > 8;
const base_fee = Number(base_fee_mtokens) / 1000;
const max_htlc = Number(max_htlc_mtokens) / 1000;
const min_htlc = Number(min_htlc_mtokens) / 1000;
const [newBaseFee, setBaseFee] = useState(base_fee);
const [newFeeRate, setFeeRate] = useState(fee_rate);
const [newCLTV, setCLTV] = useState(cltv_delta);
const [newMax, setMax] = useState(max_htlc);
const [newMin, setMin] = useState(min_htlc);
const withChanges =
newBaseFee !== base_fee ||
newFeeRate !== fee_rate ||
newCLTV !== cltv_delta ||
newMax !== max_htlc ||
newMin !== min_htlc;
const [updateFees] = useUpdateFeesMutation({
onError: error => {
toast.error(getErrorContent(error));
},
onCompleted: data => {
data.updateFees
? toast.success('Channel fees updated')
: toast.error('Error updating channel fees');
callback();
},
refetchQueries: ['GetChannels', 'ChannelFees'],
});
return (
<>
<InputWithDeco
title={'Base Fee'}
value={newBaseFee}
placeholder={'sats'}
amount={newBaseFee}
override={'sat'}
inputType={'number'}
inputCallback={value => setBaseFee(Number(value))}
/>
<InputWithDeco
title={'Fee Rate'}
value={newFeeRate}
placeholder={'ppm'}
amount={newFeeRate}
override={'ppm'}
inputType={'number'}
inputCallback={value => setFeeRate(Number(value))}
/>
<InputWithDeco
title={'CLTV Delta'}
value={newCLTV}
placeholder={'cltv delta'}
customAmount={newCLTV?.toString() || ''}
inputType={'number'}
inputCallback={value => setCLTV(Number(value))}
/>
{canMax && (
<InputWithDeco
title={'Max HTLC'}
value={newMax}
placeholder={'sats'}
amount={newMax}
override={'sat'}
inputType={'number'}
inputCallback={value => setMax(Number(value))}
/>
)}
{canMin && (
<InputWithDeco
title={'Min HTLC'}
value={newMin}
placeholder={'sats'}
amount={newMin}
override={'sat'}
inputType={'number'}
inputCallback={value => setMin(Number(value))}
/>
)}
<ColorButton
onClick={() =>
updateFees({
variables: {
transaction_id,
transaction_vout,
...(newBaseFee !== 0 && {
base_fee_tokens: newBaseFee,
}),
...(newFeeRate !== 0 && {
fee_rate: newFeeRate,
}),
...(newCLTV !== 0 && { cltv_delta: newCLTV }),
...(newMax !== 0 &&
canMax && { max_htlc_mtokens: (newMax * 1000).toString() }),
...(newMin !== 0 &&
canMin && { min_htlc_mtokens: (newMin * 1000).toString() }),
},
})
}
disabled={!withChanges}
fullWidth={true}
withMargin={'16px 0 0'}
>
Update Channel Details
</ColorButton>
</>
);
};

View file

@ -20,7 +20,7 @@ import {
import { Input } from '../../input/Input';
interface CloseChannelProps {
setModalOpen: (status: boolean) => void;
callback: () => void;
channelId: string;
channelName: string;
}
@ -37,7 +37,7 @@ const CenterLine = styled(SingleLine)`
`;
export const CloseChannel = ({
setModalOpen,
callback,
channelId,
channelName,
}: CloseChannelProps) => {
@ -64,11 +64,11 @@ export const CloseChannel = ({
useEffect(() => {
if (data && data.closeChannel) {
toast.success('Channel Closed');
setModalOpen(false);
callback();
}
}, [data, setModalOpen]);
}, [data, callback]);
const handleOnlyClose = () => setModalOpen(false);
const handleOnlyClose = () => callback();
const renderButton = (
onClick: () => void,

View file

@ -14,6 +14,7 @@ import {
useRebalanceState,
useRebalanceDispatch,
} from 'src/context/RebalanceContext';
import { ChangeDetails } from 'src/components/modal/changeDetails/ChangeDetails';
import {
getPercent,
formatSeconds,
@ -29,7 +30,6 @@ import {
SubCard,
Separation,
Sub4Title,
RightAlign,
ResponsiveLine,
DarkSubTitle,
} from '../../../components/generic/Styled';
@ -98,7 +98,7 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
const { inChannel, outChannel } = useRebalanceState();
const { channelBarType, channelBarStyle } = useConfigState();
const [modalOpen, setModalOpen] = useState(false);
const [modalOpen, setModalOpen] = useState<string>('none');
const { currency, displayValues } = useConfigState();
const priceContext = usePriceState();
@ -187,6 +187,8 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
const maxRate = Math.min(fee_rate || 0, 10000);
const feeRate = format({ amount: fee_rate, override: 'ppm' });
const maxNodeRate = Math.min(node_rate || 0, 10000);
const nodeFeeRate = format({ amount: node_rate, override: 'ppm' });
const max_htlc = Number(max_htlc_mtokens) / 1000;
@ -234,6 +236,14 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
{renderLine('CTLV Delta:', node_cltv)}
{renderLine('Max HTLC (sats)', formatSats(max_htlc))}
{renderLine('Min HTLC (sats)', formatSats(min_htlc))}
<ColorButton
fullWidth={true}
withBorder={true}
arrow={true}
onClick={() => setModalOpen('details')}
>
Update Details
</ColorButton>
<Separation />
{renderLine('Local Balance:', formatLocal)}
{renderLine('Remote Balance:', formatRemote)}
@ -263,15 +273,14 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
<Sub4Title>Partner Node Info</Sub4Title>
{renderPartner()}
<Separation />
<RightAlign>
<ColorButton
withBorder={true}
arrow={true}
onClick={() => setModalOpen(true)}
>
Close Channel
</ColorButton>
</RightAlign>
<ColorButton
fullWidth={true}
withBorder={true}
arrow={true}
onClick={() => setModalOpen('close')}
>
Close Channel
</ColorButton>
</>
);
};
@ -281,31 +290,16 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
case 'fees':
return (
<ChannelStatsColumn>
<ChannelStatsLine>
{fee_rate && (
<ProgressBar
order={fee_rate > maxRate ? 7 : 3}
percent={getBar(maxRate, biggestRateFee)}
/>
)}
<ProgressBar
order={4}
percent={getBar(biggestRateFee - maxRate, biggestRateFee)}
/>
</ChannelStatsLine>
<ChannelStatsLine>
<ProgressBar
order={1}
percent={getBar(Number(base_fee_mtokens), biggestBaseFee)}
/>
<ProgressBar
order={4}
percent={getBar(
biggestBaseFee - Number(base_fee_mtokens),
biggestBaseFee
)}
/>
</ChannelStatsLine>
<BalanceBars
local={getBar(maxNodeRate, biggestRateFee)}
remote={getBar(maxRate, biggestRateFee)}
height={10}
/>
<BalanceBars
local={getBar(Number(node_base), biggestBaseFee)}
remote={getBar(Number(base_fee_mtokens), biggestBaseFee)}
height={10}
/>
</ChannelStatsColumn>
);
case 'size':
@ -364,7 +358,10 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
case 'fees':
return (
<>
{renderLine('Fee Rate', nodeFeeRate)}
{renderLine('Partner Fee Rate', feeRate)}
<Separation />
{renderLine('Base Fee', nodeBaseFee)}
{renderLine('Partner Base Fee', baseFee)}
</>
);
@ -488,12 +485,28 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
>
{renderBarsInfo()}
</ReactTooltip>
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
<CloseChannel
setModalOpen={setModalOpen}
channelId={id}
channelName={alias}
/>
<Modal
isOpen={modalOpen !== 'none'}
closeCallback={() => setModalOpen('none')}
>
{modalOpen === 'close' ? (
<CloseChannel
callback={() => setModalOpen('none')}
channelId={id}
channelName={alias}
/>
) : (
<ChangeDetails
callback={() => setModalOpen('none')}
transaction_id={transaction_id}
transaction_vout={transaction_vout}
base_fee_mtokens={node_base}
max_htlc_mtokens={max_htlc_mtokens}
min_htlc_mtokens={min_htlc_mtokens}
fee_rate={node_rate}
cltv_delta={node_cltv}
/>
)}
</Modal>
</SubCard>
);

View file

@ -92,7 +92,7 @@ export const ChannelManage = () => {
selected={channelBarType === 'fees'}
onClick={() => changeType('fees')}
>
Partner Fees
Fees
</SingleButton>
</MultiButton>
</MarginLine>

View file

@ -73,6 +73,9 @@ export const Channels: React.FC = () => {
const { base_fee_mtokens, fee_rate } =
partner_fee_info?.channel?.partner_node_policies || {};
const { base_fee_mtokens: nodeBase, fee_rate: nodeRate } =
partner_fee_info?.channel?.node_policies || {};
const partner = Number(capacity) || 0;
const channels = Number(channel_count) || 0;
@ -90,9 +93,15 @@ export const Channels: React.FC = () => {
if (Number(base_fee_mtokens) > biggestBaseFee) {
biggestBaseFee = Number(base_fee_mtokens);
}
if (Number(nodeBase) > biggestBaseFee) {
biggestBaseFee = Number(nodeBase);
}
if (fee_rate && fee_rate > biggestRateFee) {
biggestRateFee = fee_rate;
}
if (nodeRate && nodeRate > biggestRateFee) {
biggestRateFee = nodeRate;
}
}
const getChannels = (): ChannelType[] => {

View file

@ -1,13 +1,11 @@
import React, { useState } from 'react';
import { toast } from 'react-toastify';
import { ChevronRight, AlertCircle } from 'react-feather';
import { useUpdateFeesMutation } from 'src/graphql/mutations/__generated__/updateFees.generated';
import { InputWithDeco } from 'src/components/input/InputWithDeco';
import { AlertCircle } from 'react-feather';
import { ChannelType } from 'src/graphql/types';
import { formatSats } from 'src/utils/helpers';
import { chartColors } from 'src/styles/Themes';
import { ColorButton } from 'src/components/buttons/colorButton/ColorButton';
import { useNodeInfo } from 'src/hooks/UseNodeInfo';
import { ChangeDetails } from 'src/components/modal/changeDetails/ChangeDetails';
import Modal from 'src/components/modal/ReactModal';
import {
SubCard,
Separation,
@ -18,7 +16,6 @@ import {
} from '../../components/generic/Styled';
import { renderLine, getWithCopy } from '../../components/generic/helpers';
import { MainInfo, NodeTitle } from '../../components/generic/CardGeneric';
import { getErrorContent } from '../../utils/error';
import { WarningText } from '../stats/styles';
import { FeeCardColumn, FeeCardNoWrap } from './styles';
@ -35,9 +32,7 @@ export const FeeCard: React.FC<FeeCardProps> = ({
setIndexOpen,
indexOpen,
}) => {
const { minorVersion, revision } = useNodeInfo();
const canMax = (minorVersion === 7 && revision > 1) || minorVersion > 7;
const canMin = (minorVersion === 8 && revision > 2) || minorVersion > 8;
const [modalOpen, setModalOpen] = useState(false);
const { partner_public_key, partner_node_info, partner_fee_info } = channel;
@ -60,37 +55,6 @@ export const FeeCard: React.FC<FeeCardProps> = ({
const max_htlc = Number(max_htlc_mtokens) / 1000;
const min_htlc = Number(min_htlc_mtokens) / 1000;
const [newBaseFee, setBaseFee] = useState(base_fee);
const [newFeeRate, setFeeRate] = useState(fee_rate);
const [newCLTV, setCLTV] = useState(cltv_delta);
const [newMax, setMax] = useState(max_htlc);
const [newMin, setMin] = useState(min_htlc);
const withChanges =
newBaseFee !== base_fee ||
newFeeRate !== fee_rate ||
newCLTV !== cltv_delta ||
newMax !== max_htlc ||
newMin !== min_htlc;
const [updateFees] = useUpdateFeesMutation({
onError: error => {
setBaseFee(base_fee);
setFeeRate(fee_rate);
setCLTV(cltv_delta);
setMax(max_htlc);
setMin(min_htlc);
toast.error(getErrorContent(error));
},
onCompleted: data => {
setIndexOpen(0);
data.updateFees
? toast.success('Channel fees updated')
: toast.error('Error updating channel fees');
},
refetchQueries: ['ChannelFees'],
});
const handleClick = () => {
if (indexOpen === index) {
setIndexOpen(0);
@ -129,87 +93,19 @@ export const FeeCard: React.FC<FeeCardProps> = ({
return (
<>
{renderWarningText(cltv_delta)}
<ColorButton
withBorder={true}
onClick={() => setModalOpen(true)}
fullWidth={true}
withMargin={'16px 0 0'}
arrow={true}
>
Update Details
</ColorButton>
{renderPartnerDetails()}
<Separation />
{renderLine('Transaction Id:', getWithCopy(transaction_id))}
{renderLine('Transaction Vout:', transaction_vout)}
<Separation />
<InputWithDeco
title={'Base Fee'}
value={newBaseFee}
placeholder={'sats'}
amount={newBaseFee}
override={'sat'}
inputType={'number'}
inputCallback={value => setBaseFee(Number(value))}
/>
<InputWithDeco
title={'Fee Rate'}
value={newFeeRate}
placeholder={'ppm'}
amount={newFeeRate}
override={'ppm'}
inputType={'number'}
inputCallback={value => setFeeRate(Number(value))}
/>
<InputWithDeco
title={'CLTV Delta'}
value={newCLTV}
placeholder={'cltv delta'}
customAmount={newCLTV?.toString() || ''}
inputType={'number'}
inputCallback={value => setCLTV(Number(value))}
/>
{canMax && (
<InputWithDeco
title={'Max HTLC'}
value={newMax}
placeholder={'sats'}
amount={newMax}
override={'sat'}
inputType={'number'}
inputCallback={value => setMax(Number(value))}
/>
)}
{canMin && (
<InputWithDeco
title={'Min HTLC'}
value={newMin}
placeholder={'sats'}
amount={newMin}
override={'sat'}
inputType={'number'}
inputCallback={value => setMin(Number(value))}
/>
)}
<ColorButton
onClick={() =>
updateFees({
variables: {
transaction_id,
transaction_vout,
...(newBaseFee !== 0 && {
base_fee_tokens: newBaseFee,
}),
...(newFeeRate !== 0 && {
fee_rate: newFeeRate,
}),
...(newCLTV !== 0 && { cltv_delta: newCLTV }),
...(newMax !== 0 &&
canMax && { max_htlc_mtokens: (newMax * 1000).toString() }),
...(newMin !== 0 &&
canMin && { min_htlc_mtokens: (newMin * 1000).toString() }),
},
})
}
disabled={!withChanges}
fullWidth={true}
withMargin={'16px 0 0'}
>
Update Channel Details
<ChevronRight size={18} />
</ColorButton>
</>
);
};
@ -304,6 +200,18 @@ export const FeeCard: React.FC<FeeCardProps> = ({
</ResponsiveLine>
</MainInfo>
{index === indexOpen && renderDetails()}
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
<ChangeDetails
callback={() => setModalOpen(false)}
transaction_id={transaction_id}
transaction_vout={transaction_vout}
base_fee_mtokens={base_fee_mtokens}
max_htlc_mtokens={max_htlc_mtokens}
min_htlc_mtokens={min_htlc_mtokens}
fee_rate={fee_rate}
cltv_delta={cltv_delta}
/>
</Modal>
</SubCard>
);
};