mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-22 22:25:21 +01:00
feat: ✨ balance from channel view
This commit is contained in:
parent
76aacd3c52
commit
830586b6bd
15 changed files with 361 additions and 55 deletions
|
@ -2,20 +2,18 @@ import * as React from 'react';
|
|||
import { ThemeProvider } from 'styled-components';
|
||||
import { ModalProvider, BaseModalBackground } from 'styled-react-modal';
|
||||
import { useRouter } from 'next/router';
|
||||
import { toast } from 'react-toastify';
|
||||
import Head from 'next/head';
|
||||
import { StyledToastContainer } from 'src/components/toastContainer/ToastContainer';
|
||||
import { ContextProvider } from '../src/context/ContextProvider';
|
||||
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
|
||||
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
||||
import { Header } from '../src/layouts/header/Header';
|
||||
import { Footer } from '../src/layouts/footer/Footer';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import 'react-toastify/dist/ReactToastify.min.css';
|
||||
import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled';
|
||||
import { parseCookies } from '../src/utils/cookies';
|
||||
import 'react-circular-progressbar/dist/styles.css';
|
||||
|
||||
toast.configure({ draggable: false, pauseOnFocusLoss: false });
|
||||
|
||||
const Wrapper: React.FC = ({ children }) => {
|
||||
const { theme } = useConfigState();
|
||||
const { pathname } = useRouter();
|
||||
|
@ -50,6 +48,7 @@ const App = ({ Component, pageProps, initialConfig }) => (
|
|||
</Wrapper>
|
||||
</ContextProvider>
|
||||
</ConfigProvider>
|
||||
<StyledToastContainer />
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
|
@ -76,14 +76,14 @@ export const bosResolvers = {
|
|||
const lnd = getLnd(auth, context);
|
||||
|
||||
const filteredParams = {
|
||||
...(avoid.length > 0 && { avoid }),
|
||||
avoid,
|
||||
out_channels,
|
||||
...(in_through && { in_through }),
|
||||
...(is_avoiding_high_inbound && { is_avoiding_high_inbound }),
|
||||
...(max_fee > 0 && { max_fee }),
|
||||
...(max_fee_rate > 0 && { max_fee_rate }),
|
||||
...(max_rebalance > 0 && { max_rebalance }),
|
||||
...(node && { node }),
|
||||
...(out_channels.length > 0 && { out_channels }),
|
||||
...(out_through && { out_through }),
|
||||
...(target && { target }),
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import styled from 'styled-components';
|
||||
import styled, { css } from 'styled-components';
|
||||
import {
|
||||
progressBackground,
|
||||
mediaWidths,
|
||||
|
@ -65,8 +65,16 @@ export const StatusLine = styled.div`
|
|||
margin: 0 0 -8px 0;
|
||||
`;
|
||||
|
||||
export const MainInfo = styled.div`
|
||||
type MainProps = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const MainInfo = styled.div<MainProps>`
|
||||
${({ disabled }) =>
|
||||
!disabled &&
|
||||
css`
|
||||
cursor: pointer;
|
||||
`}
|
||||
`;
|
||||
|
||||
export const StatusDot = styled.div`
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import styled from 'styled-components';
|
||||
import { chartColors } from 'src/styles/Themes';
|
||||
import { chartColors, mediaWidths } from 'src/styles/Themes';
|
||||
|
||||
export const BetaNotification = styled.div`
|
||||
width: 100%;
|
||||
|
@ -9,4 +9,9 @@ export const BetaNotification = styled.div`
|
|||
color: black;
|
||||
margin-bottom: 16px;
|
||||
padding: 4px 0;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
`;
|
||||
|
|
22
src/components/toastContainer/ToastContainer.ts
Normal file
22
src/components/toastContainer/ToastContainer.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import styled from 'styled-components';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import { themeColors, chartColors } from 'src/styles/Themes';
|
||||
|
||||
export const StyledToastContainer = styled(ToastContainer)`
|
||||
.Toastify__toast {
|
||||
border-radius: 4px;
|
||||
background-color: ${themeColors.blue2};
|
||||
}
|
||||
.Toastify__toast--error {
|
||||
border-radius: 4px;
|
||||
background-color: ${chartColors.red};
|
||||
}
|
||||
.Toastify__toast--warning {
|
||||
border-radius: 4px;
|
||||
background-color: ${chartColors.orange};
|
||||
}
|
||||
.Toastify__toast--success {
|
||||
border-radius: 4px;
|
||||
background-color: ${chartColors.green};
|
||||
}
|
||||
`;
|
|
@ -6,7 +6,11 @@ import omit from 'lodash.omit';
|
|||
const themeTypes = ['dark', 'light'];
|
||||
const currencyTypes = ['sat', 'btc', 'fiat'];
|
||||
|
||||
export type channelBarStyleTypes = 'normal' | 'compact' | 'ultracompact';
|
||||
export type channelBarStyleTypes =
|
||||
| 'normal'
|
||||
| 'compact'
|
||||
| 'ultracompact'
|
||||
| 'balancing';
|
||||
export type channelBarTypeTypes = 'balance' | 'fees' | 'size' | 'proportional';
|
||||
export type channelSortTypes =
|
||||
| 'none'
|
||||
|
|
|
@ -4,13 +4,16 @@ import { BitcoinInfoProvider } from './BitcoinContext';
|
|||
import { StatusProvider } from './StatusContext';
|
||||
import { PriceProvider } from './PriceContext';
|
||||
import { ChatProvider } from './ChatContext';
|
||||
import { RebalanceProvider } from './RebalanceContext';
|
||||
|
||||
export const ContextProvider: React.FC = ({ children }) => (
|
||||
<AccountProvider>
|
||||
<BitcoinInfoProvider>
|
||||
<PriceProvider>
|
||||
<ChatProvider>
|
||||
<StatusProvider>{children}</StatusProvider>
|
||||
<StatusProvider>
|
||||
<RebalanceProvider>{children}</RebalanceProvider>
|
||||
</StatusProvider>
|
||||
</ChatProvider>
|
||||
</PriceProvider>
|
||||
</BitcoinInfoProvider>
|
||||
|
|
75
src/context/RebalanceContext.tsx
Normal file
75
src/context/RebalanceContext.tsx
Normal file
|
@ -0,0 +1,75 @@
|
|||
import React, { createContext, useContext, useReducer } from 'react';
|
||||
import { ChannelType } from 'src/graphql/types';
|
||||
|
||||
type State = {
|
||||
inChannel: ChannelType | null;
|
||||
outChannel: ChannelType | null;
|
||||
};
|
||||
|
||||
type ActionType =
|
||||
| {
|
||||
type: 'setIn';
|
||||
channel: ChannelType | null;
|
||||
}
|
||||
| {
|
||||
type: 'setOut';
|
||||
channel: ChannelType | null;
|
||||
}
|
||||
| {
|
||||
type: 'clear';
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
export const StateContext = createContext<State | undefined>(undefined);
|
||||
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const initialState = {
|
||||
inChannel: null,
|
||||
outChannel: null,
|
||||
};
|
||||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
switch (action.type) {
|
||||
case 'setIn':
|
||||
return { ...state, inChannel: action.channel };
|
||||
case 'setOut':
|
||||
return { ...state, outChannel: action.channel };
|
||||
case 'clear':
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const RebalanceProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState);
|
||||
|
||||
return (
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
||||
</DispatchContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useRebalanceState = () => {
|
||||
const context = useContext(StateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
'useRebalanceState must be used within a RebalanceProvider'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
const useRebalanceDispatch = () => {
|
||||
const context = useContext(DispatchContext);
|
||||
if (context === undefined) {
|
||||
throw new Error(
|
||||
'useRebalanceDispatch must be used within a RebalanceProvider'
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { RebalanceProvider, useRebalanceState, useRebalanceDispatch };
|
|
@ -16,6 +16,10 @@ import { chartColors } from 'src/styles/Themes';
|
|||
import { ViewSwitch } from 'src/components/viewSwitch/ViewSwitch';
|
||||
import { useMutationResultWithReset } from 'src/hooks/UseMutationWithReset';
|
||||
import { BetaNotification } from 'src/components/notification/Beta';
|
||||
import {
|
||||
useRebalanceState,
|
||||
useRebalanceDispatch,
|
||||
} from 'src/context/RebalanceContext';
|
||||
import { AdvancedResult } from './AdvancedResult';
|
||||
import { ModalNodes } from './Modals/ModalNodes';
|
||||
import { ModalChannels } from './Modals/ModalChannels';
|
||||
|
@ -165,7 +169,29 @@ const SettingLine: React.FC<{ title: string }> = ({ children, title }) => (
|
|||
export const AdvancedBalance = () => {
|
||||
const [openType, openTypeSet] = React.useState<string>('none');
|
||||
const [isDetailed, isDetailedSet] = React.useState<boolean>(false);
|
||||
const [state, dispatch] = React.useReducer(reducer, initialState);
|
||||
|
||||
const rebalanceDispatch = useRebalanceDispatch();
|
||||
const { inChannel, outChannel } = useRebalanceState();
|
||||
|
||||
const in_through = inChannel
|
||||
? {
|
||||
alias: inChannel.partner_node_info?.node?.alias,
|
||||
id: inChannel.partner_public_key,
|
||||
}
|
||||
: defaultRebalanceId;
|
||||
|
||||
const out_through = outChannel
|
||||
? {
|
||||
alias: outChannel.partner_node_info?.node?.alias,
|
||||
id: outChannel.partner_public_key,
|
||||
}
|
||||
: defaultRebalanceId;
|
||||
|
||||
const [state, dispatch] = React.useReducer(reducer, {
|
||||
...initialState,
|
||||
in_through,
|
||||
out_through,
|
||||
});
|
||||
|
||||
const [rebalance, { data: _data, loading }] = useBosRebalanceMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
|
@ -231,21 +257,6 @@ export const AdvancedBalance = () => {
|
|||
{hasAvoid ? <Minus size={18} /> : <Plus size={18} />}
|
||||
</ColorButton>
|
||||
</SettingLine>
|
||||
<SettingLine title={'Decrease Inbound Of'}>
|
||||
{hasInChannel ? (
|
||||
<RebalanceTag>{state.in_through.alias}</RebalanceTag>
|
||||
) : null}
|
||||
<ColorButton
|
||||
color={hasInChannel ? chartColors.red : undefined}
|
||||
onClick={() =>
|
||||
hasInChannel
|
||||
? dispatch({ type: 'inChannel', channel: defaultRebalanceId })
|
||||
: openTypeSet('inChannel')
|
||||
}
|
||||
>
|
||||
{hasInChannel ? <Minus size={18} /> : <Plus size={18} />}
|
||||
</ColorButton>
|
||||
</SettingLine>
|
||||
{!hasOutChannels && (
|
||||
<SettingLine title={'Increase Inbound Of'}>
|
||||
{hasOutChannel ? (
|
||||
|
@ -253,19 +264,40 @@ export const AdvancedBalance = () => {
|
|||
) : null}
|
||||
<ColorButton
|
||||
color={hasOutChannel ? chartColors.red : undefined}
|
||||
onClick={() =>
|
||||
hasOutChannel
|
||||
? dispatch({
|
||||
onClick={() => {
|
||||
if (hasOutChannel) {
|
||||
rebalanceDispatch({ type: 'setOut', channel: null });
|
||||
dispatch({
|
||||
type: 'outChannel',
|
||||
channel: defaultRebalanceId,
|
||||
})
|
||||
: openTypeSet('outChannel')
|
||||
});
|
||||
} else {
|
||||
openTypeSet('outChannel');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{hasOutChannel ? <Minus size={18} /> : <Plus size={18} />}
|
||||
</ColorButton>
|
||||
</SettingLine>
|
||||
)}
|
||||
<SettingLine title={'Decrease Inbound Of'}>
|
||||
{hasInChannel ? (
|
||||
<RebalanceTag>{state.in_through.alias}</RebalanceTag>
|
||||
) : null}
|
||||
<ColorButton
|
||||
color={hasInChannel ? chartColors.red : undefined}
|
||||
onClick={() => {
|
||||
if (hasInChannel) {
|
||||
rebalanceDispatch({ type: 'setIn', channel: null });
|
||||
dispatch({ type: 'inChannel', channel: defaultRebalanceId });
|
||||
} else {
|
||||
openTypeSet('inChannel');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{hasInChannel ? <Minus size={18} /> : <Plus size={18} />}
|
||||
</ColorButton>
|
||||
</SettingLine>
|
||||
{!hasOutChannel && (
|
||||
<SettingLine title={'Out Through Channels'}>
|
||||
{hasOutChannels && (
|
||||
|
|
|
@ -74,6 +74,13 @@ export const RebalanceTag = styled.div`
|
|||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
font-size: 14px;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
max-width: 80px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
export const RebalanceLine = styled(SingleLine)`
|
||||
|
|
|
@ -18,16 +18,19 @@ import { Input } from 'src/components/input/Input';
|
|||
import { BalanceCard } from 'src/views/balance/BalanceCard';
|
||||
import { BalanceRoute } from 'src/views/balance/BalanceRoute';
|
||||
import { Price } from 'src/components/price/Price';
|
||||
import {
|
||||
useRebalanceState,
|
||||
useRebalanceDispatch,
|
||||
} from 'src/context/RebalanceContext';
|
||||
|
||||
export const SimpleBalance = () => {
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const [outgoing, setOutgoing] = useState<ChannelType | null>();
|
||||
const [incoming, setIncoming] = useState<ChannelType | null>();
|
||||
const dispatch = useRebalanceDispatch();
|
||||
const { inChannel: incoming, outChannel: outgoing } = useRebalanceState();
|
||||
|
||||
const [amount, setAmount] = useState<number>();
|
||||
|
||||
const [maxFee, setMaxFee] = useState<number>();
|
||||
|
||||
const [blocked, setBlocked] = useState(false);
|
||||
|
||||
const { loading, data } = useGetChannelsQuery({
|
||||
|
@ -43,17 +46,15 @@ export const SimpleBalance = () => {
|
|||
const handleReset = (type: string) => {
|
||||
switch (type) {
|
||||
case 'outgoing':
|
||||
setOutgoing(undefined);
|
||||
setIncoming(undefined);
|
||||
dispatch({ type: 'clear' });
|
||||
break;
|
||||
case 'incoming':
|
||||
setIncoming(undefined);
|
||||
dispatch({ type: 'setIn', channel: null });
|
||||
break;
|
||||
case 'all':
|
||||
dispatch({ type: 'clear' });
|
||||
setMaxFee(undefined);
|
||||
setAmount(undefined);
|
||||
setOutgoing(undefined);
|
||||
setIncoming(undefined);
|
||||
setBlocked(false);
|
||||
break;
|
||||
default:
|
||||
|
@ -91,8 +92,9 @@ export const SimpleBalance = () => {
|
|||
}
|
||||
|
||||
const callback = isOutgoing
|
||||
? !outgoing && { callback: () => setOutgoing(channel) }
|
||||
: outgoing && !incoming && { callback: () => setIncoming(channel) };
|
||||
? !outgoing && { callback: () => dispatch({ type: 'setOut', channel }) }
|
||||
: outgoing &&
|
||||
!incoming && { callback: () => dispatch({ type: 'setIn', channel }) };
|
||||
|
||||
return (
|
||||
<BalanceCard
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import styled from 'styled-components';
|
||||
import { mediaWidths } from 'src/styles/Themes';
|
||||
import styled, { css } from 'styled-components';
|
||||
import {
|
||||
mediaWidths,
|
||||
colorButtonBackground,
|
||||
textColor,
|
||||
colorButtonBorder,
|
||||
chartColors,
|
||||
disabledTextColor,
|
||||
} from 'src/styles/Themes';
|
||||
|
||||
export const ChannelIconPadding = styled.div`
|
||||
display: flex;
|
||||
|
@ -21,11 +28,11 @@ export const ChannelStatsLine = styled.div`
|
|||
export const ChannelBarSide = styled.div`
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -53,3 +60,58 @@ export const IconCursor = styled.div`
|
|||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
`;
|
||||
|
||||
export const ChannelBalanceRow = styled.div`
|
||||
display: flex;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
type BalanceButtonProps = {
|
||||
selected?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const ChannelBalanceButton = styled.button<BalanceButtonProps>`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
outline: none;
|
||||
padding: 6px 8px;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
margin-left: 8px;
|
||||
color: ${({ disabled }) => (disabled ? disabledTextColor : textColor)};
|
||||
${({ disabled }) =>
|
||||
!disabled &&
|
||||
css`
|
||||
cursor: pointer;
|
||||
`}
|
||||
background-color: ${({ selected }) =>
|
||||
selected ? chartColors.orange : colorButtonBackground};
|
||||
|
||||
@media(${mediaWidths.mobile}) {
|
||||
margin: 8px 8px 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:hover {
|
||||
background-color: ${({ selected, disabled }) =>
|
||||
disabled
|
||||
? colorButtonBackground
|
||||
: selected
|
||||
? chartColors.orange2
|
||||
: colorButtonBorder};
|
||||
}
|
||||
`;
|
||||
|
||||
export const ChannelGoToToast = styled.div`
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
`;
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
import React, { useState } from 'react';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { ArrowDown, ArrowUp, EyeOff } from 'react-feather';
|
||||
import {
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
EyeOff,
|
||||
ChevronsUp,
|
||||
ChevronsDown,
|
||||
X,
|
||||
} from 'react-feather';
|
||||
import { ChannelType } from 'src/graphql/types';
|
||||
import { BalanceBars } from 'src/components/balance';
|
||||
import {
|
||||
useRebalanceState,
|
||||
useRebalanceDispatch,
|
||||
} from 'src/context/RebalanceContext';
|
||||
import { getPercent, formatSeconds } from '../../../utils/helpers';
|
||||
import {
|
||||
ProgressBar,
|
||||
|
@ -40,6 +51,8 @@ import {
|
|||
ChannelStatsColumn,
|
||||
ChannelSingleLine,
|
||||
ChannelStatsLine,
|
||||
ChannelBalanceRow,
|
||||
ChannelBalanceButton,
|
||||
} from './Channel.style';
|
||||
|
||||
const getSymbol = (status: boolean) => {
|
||||
|
@ -78,6 +91,9 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
|
|||
biggestBaseFee,
|
||||
biggestRateFee,
|
||||
}) => {
|
||||
const dispatch = useRebalanceDispatch();
|
||||
const { inChannel, outChannel } = useRebalanceState();
|
||||
|
||||
const { channelBarType, channelBarStyle } = useConfigState();
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
|
@ -114,6 +130,9 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
|
|||
partner_fee_info,
|
||||
} = channelInfo;
|
||||
|
||||
const isIn = inChannel?.id === id;
|
||||
const isOut = outChannel?.id === id;
|
||||
|
||||
const {
|
||||
alias,
|
||||
capacity: partnerNodeCapacity = 0,
|
||||
|
@ -340,6 +359,7 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
|
|||
const getSubCardProps = () => {
|
||||
switch (channelBarStyle) {
|
||||
case 'ultracompact':
|
||||
case 'balancing':
|
||||
return {
|
||||
withMargin: '0 0 4px 0',
|
||||
padding: index === indexOpen ? '0 0 16px' : '2px 0',
|
||||
|
@ -357,7 +377,10 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
|
|||
|
||||
return (
|
||||
<SubCard key={`${index}-${id}`} noCard={true} {...getSubCardProps()}>
|
||||
<MainInfo onClick={() => handleClick()}>
|
||||
<MainInfo
|
||||
disabled={channelBarStyle === 'balancing'}
|
||||
onClick={() => channelBarStyle !== 'balancing' && handleClick()}
|
||||
>
|
||||
{channelBarStyle === 'normal' && (
|
||||
<StatusLine>
|
||||
{getStatusDot(is_active, 'active')}
|
||||
|
@ -368,7 +391,10 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
|
|||
<ResponsiveLine>
|
||||
<ChannelNodeTitle style={{ flexGrow: 2 }}>
|
||||
{alias || partner_public_key?.substring(0, 6)}
|
||||
{channelBarStyle !== 'ultracompact' && (
|
||||
{!(
|
||||
channelBarStyle === 'ultracompact' ||
|
||||
channelBarStyle === 'balancing'
|
||||
) && (
|
||||
<ChannelSingleLine>
|
||||
<DarkSubTitle>{formatBalance}</DarkSubTitle>
|
||||
<ChannelIconPadding>
|
||||
|
@ -380,6 +406,32 @@ export const ChannelCard: React.FC<ChannelCardProps> = ({
|
|||
</ChannelNodeTitle>
|
||||
<ChannelBarSide data-tip data-for={`node_balance_tip_${index}`}>
|
||||
{renderBars()}
|
||||
{channelBarStyle === 'balancing' && (
|
||||
<ChannelBalanceRow>
|
||||
<ChannelBalanceButton
|
||||
selected={isOut}
|
||||
disabled={isIn}
|
||||
onClick={() =>
|
||||
isOut
|
||||
? dispatch({ type: 'setOut', channel: null })
|
||||
: dispatch({ type: 'setOut', channel: channelInfo })
|
||||
}
|
||||
>
|
||||
{isOut ? <X size={16} /> : <ChevronsUp size={16} />}
|
||||
</ChannelBalanceButton>
|
||||
<ChannelBalanceButton
|
||||
selected={isIn}
|
||||
disabled={isOut}
|
||||
onClick={() =>
|
||||
isIn
|
||||
? dispatch({ type: 'setIn', channel: null })
|
||||
: dispatch({ type: 'setIn', channel: channelInfo })
|
||||
}
|
||||
>
|
||||
{isIn ? <X size={16} /> : <ChevronsDown size={16} />}
|
||||
</ChannelBalanceButton>
|
||||
</ChannelBalanceRow>
|
||||
)}
|
||||
</ChannelBarSide>
|
||||
</ResponsiveLine>
|
||||
</MainInfo>
|
||||
|
|
|
@ -59,6 +59,12 @@ export const ChannelManage = () => {
|
|||
>
|
||||
Ultra-Compact
|
||||
</SingleButton>
|
||||
<SingleButton
|
||||
selected={channelBarStyle === 'balancing'}
|
||||
onClick={() => changeStyle('balancing')}
|
||||
>
|
||||
Balancing
|
||||
</SingleButton>
|
||||
</MultiButton>
|
||||
</MarginLine>
|
||||
<MarginLine>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useGetChannelsQuery } from 'src/graphql/queries/__generated__/getChannels.generated';
|
||||
|
@ -6,17 +6,46 @@ import { useConfigState } from 'src/context/ConfigContext';
|
|||
import { sortBy } from 'underscore';
|
||||
import { getPercent } from 'src/utils/helpers';
|
||||
import { ChannelType } from 'src/graphql/types';
|
||||
import { useRebalanceState } from 'src/context/RebalanceContext';
|
||||
import { useRouter } from 'next/router';
|
||||
import { appendBasePath } from 'src/utils/basePath';
|
||||
import { Card } from '../../../components/generic/Styled';
|
||||
import { getErrorContent } from '../../../utils/error';
|
||||
import { LoadingCard } from '../../../components/loading/LoadingCard';
|
||||
import { ChannelCard } from './ChannelCard';
|
||||
import { ChannelGoToToast } from './Channel.style';
|
||||
|
||||
export const Channels: React.FC = () => {
|
||||
const toastId = useRef(null);
|
||||
const { push } = useRouter();
|
||||
|
||||
const { sortDirection, channelSort } = useConfigState();
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { inChannel, outChannel } = useRebalanceState();
|
||||
const hasIn = !!inChannel;
|
||||
const hasOut = !!outChannel;
|
||||
|
||||
useEffect(() => {
|
||||
if (hasIn && hasOut) {
|
||||
toastId.current = toast.info(
|
||||
<ChannelGoToToast>Click to go to rebalancing</ChannelGoToToast>,
|
||||
{
|
||||
position: 'bottom-right',
|
||||
autoClose: false,
|
||||
closeButton: false,
|
||||
onClick: () => push(appendBasePath('/rebalance')),
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!hasIn || !hasOut) {
|
||||
toast.dismiss(toastId.current);
|
||||
}
|
||||
return () => toast.dismiss();
|
||||
}, [hasIn, hasOut, push]);
|
||||
|
||||
const { loading, data } = useGetChannelsQuery({
|
||||
skip: !auth,
|
||||
variables: { auth },
|
||||
|
|
Loading…
Add table
Reference in a new issue