diff --git a/pages/_app.tsx b/pages/_app.tsx index b90429fd..5c8cd434 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -41,7 +41,7 @@ const Wrapper: React.FC = ({ children }) => { return <>{children}; } if (hasAccount === 'false') { - return ; + return ; } return {children}; }; @@ -100,19 +100,18 @@ const App = ({ App.getInitialProps = async props => { const cookieParam = getUrlParam(props.router?.query?.token); const cookies = parseCookies(props.ctx.req); - const defaultState = {}; if (!cookies?.config) { - return { initialConfig: {}, ...defaultState }; + return { initialConfig: {} }; } try { const config = JSON.parse(cookies.config); return { - initialConfig: { ...config, ...defaultState }, + initialConfig: config, cookieParam, }; } catch (error) { - return { initialConfig: {}, cookieParam, ...defaultState }; + return { initialConfig: {}, cookieParam }; } }; diff --git a/pages/balance.tsx b/pages/balance.tsx index 2bac30fa..2466143f 100644 --- a/pages/balance.tsx +++ b/pages/balance.tsx @@ -84,10 +84,9 @@ const BalanceView = () => { }; const renderChannels = (isOutgoing?: boolean) => { - const channels = sortBy(data.getChannels, [ - (channel: any) => - getPercent(channel.remote_balance, channel.local_balance), - ]); + const channels = sortBy(data.getChannels, channel => + getPercent(channel.remote_balance, channel.local_balance) + ); const finalChannels = isOutgoing ? channels : channels.reverse(); diff --git a/pages/channels.tsx b/pages/channels.tsx index 54bb97de..c3e8f9fe 100644 --- a/pages/channels.tsx +++ b/pages/channels.tsx @@ -2,6 +2,9 @@ import React, { useState, useEffect } from 'react'; import { useAccountState } from 'src/context/AccountContext'; import { useGetChannelAmountInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated'; import styled from 'styled-components'; +import { Settings } from 'react-feather'; +import { IconCursor } from 'src/views/channels/channels/Channel.style'; +import { ChannelManage } from 'src/views/channels/channels/ChannelManage'; import { Channels } from '../src/views/channels/channels/Channels'; import { PendingChannels } from '../src/views/channels/pendingChannels/PendingChannels'; import { ClosedChannels } from '../src/views/channels/closedChannels/ClosedChannels'; @@ -28,6 +31,7 @@ const ButtonRow = styled.div` `; const ChannelView = () => { + const [isOpen, isOpenSet] = useState(false); const [view, setView] = useState(1); const [amounts, setAmounts] = useState({ active: 0, @@ -94,8 +98,14 @@ const ChannelView = () => { setView(3)}> {`Closed (${amounts.closed})`} + {view === 1 && ( + + isOpenSet(p => !p)} /> + + )} + {view === 1 && isOpen && } {getView()} ); diff --git a/src/components/generic/CardGeneric.tsx b/src/components/generic/CardGeneric.tsx index 37b14a92..362a1484 100644 --- a/src/components/generic/CardGeneric.tsx +++ b/src/components/generic/CardGeneric.tsx @@ -27,6 +27,8 @@ export const ProgressBar = styled.div` return chartColors.green; case 3: return chartColors.orange; + case 4: + return progressBackground; default: return chartColors.purple; } diff --git a/src/components/generic/Styled.tsx b/src/components/generic/Styled.tsx index 197700cf..f05c04f2 100644 --- a/src/components/generic/Styled.tsx +++ b/src/components/generic/Styled.tsx @@ -81,13 +81,18 @@ interface SubCardProps { padding?: string; withMargin?: string; noCard?: boolean; + noBackground?: boolean; } export const SubCard = styled.div` margin: ${({ withMargin }) => (withMargin ? withMargin : '0 0 10px 0')}; padding: ${({ padding }) => (padding ? padding : '16px')}; - background: ${subCardColor}; - border: 1px solid ${cardBorderColor}; + ${({ noBackground }) => + !noBackground && + css` + background: ${subCardColor}; + border: 1px solid ${cardBorderColor}; + `} border-left: ${({ color }) => (color ? `2px solid ${color}` : '')}; &:hover { diff --git a/src/context/ConfigContext.tsx b/src/context/ConfigContext.tsx index 74816ffe..a652c397 100644 --- a/src/context/ConfigContext.tsx +++ b/src/context/ConfigContext.tsx @@ -5,6 +5,11 @@ import Cookies from 'js-cookie'; const themeTypes = ['dark', 'light']; const currencyTypes = ['sat', 'btc', 'EUR', 'USD']; +export type channelBarStyleTypes = 'normal' | 'compact' | 'ultracompact'; +export type channelBarTypeTypes = 'balance' | 'details' | 'partner'; +export type channelSortTypes = 'none' | 'local' | 'balance'; +export type sortDirectionTypes = 'increase' | 'decrease'; + type State = { currency: string; theme: string; @@ -17,7 +22,10 @@ type State = { hideNonVerified: boolean; maxFee: number; chatPollingSpeed: number; - channelBarType: 'normal' | 'partner'; + channelBarStyle: channelBarStyleTypes; + channelBarType: channelBarTypeTypes; + channelSort: channelSortTypes; + sortDirection: sortDirectionTypes; }; type ConfigInitProps = { @@ -37,7 +45,10 @@ type ActionType = { hideNonVerified?: boolean; maxFee?: number; chatPollingSpeed?: number; - channelBarType?: 'normal' | 'partner'; + channelBarStyle?: channelBarStyleTypes; + channelBarType?: channelBarTypeTypes; + channelSort?: channelSortTypes; + sortDirection?: sortDirectionTypes; }; type Dispatch = (action: ActionType) => void; @@ -65,7 +76,10 @@ const initialState: State = { hideNonVerified: false, maxFee: 20, chatPollingSpeed: 1000, - channelBarType: 'normal', + channelBarStyle: 'normal', + channelBarType: 'balance', + channelSort: 'none', + sortDirection: 'decrease', }; const stateReducer = (state: State, action: ActionType): State => { diff --git a/src/views/balance/Balance.styled.tsx b/src/views/balance/Balance.styled.tsx index 5359088e..bc5ce8de 100644 --- a/src/views/balance/Balance.styled.tsx +++ b/src/views/balance/Balance.styled.tsx @@ -34,10 +34,11 @@ export const ChannelColumnSection = styled.div` export const ChannelLineSection = styled.div` display: flex; - align-items: center; - max-width: 300px; + flex-direction: column; + width: 180px; @media (${mediaWidths.mobile}) { + align-items: center; max-width: unset; justify-content: center; width: 100%; diff --git a/src/views/balance/BalanceCard.tsx b/src/views/balance/BalanceCard.tsx index 7a45ce8b..fb17119f 100644 --- a/src/views/balance/BalanceCard.tsx +++ b/src/views/balance/BalanceCard.tsx @@ -62,7 +62,7 @@ export const BalanceCard = ({ } = channel; const { alias } = partner_node_info; - const balancedness = getPercent(remote_balance, local_balance) / 100; + const balancedness = getPercent(local_balance, remote_balance) / 100; const formatBalance = numeral(balancedness).format('0,0.00'); const props = withColor ? { color: themeColors.blue3 } : {}; @@ -72,9 +72,12 @@ export const BalanceCard = ({ callback && callback()}> - {alias && alias !== '' - ? `${alias} - ${id}` - : `${partner_public_key?.substring(0, 6)} - ${id}`} +
+ {alias && alias !== '' + ? alias + : partner_public_key?.substring(0, 6)} +
+ {id}
diff --git a/src/views/channels/channels/Channel.style.ts b/src/views/channels/channels/Channel.style.ts new file mode 100644 index 00000000..93d6d2a4 --- /dev/null +++ b/src/views/channels/channels/Channel.style.ts @@ -0,0 +1,55 @@ +import styled from 'styled-components'; +import { mediaWidths } from 'src/styles/Themes'; + +export const ChannelIconPadding = styled.div` + display: flex; + margin-left: 8px; +`; + +export const ChannelStatsColumn = styled.div` + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-end; +`; + +export const ChannelStatsLine = styled.div` + width: 100%; + display: flex; +`; + +export const ChannelBarSide = styled.div` + width: 50%; + display: flex; + flex-direction: column; + cursor: pointer; + + @media (${mediaWidths.mobile}) { + width: 100%; + } +`; + +export const ChannelNodeTitle = styled.div` + font-size: 16px; + font-weight: 700; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + @media (${mediaWidths.mobile}) { + text-align: center; + margin-bottom: 8px; + } +`; + +export const ChannelSingleLine = styled.div` + display: flex; + align-items: center; +`; + +export const IconCursor = styled.div` + display: flex; + align-items: center; + cursor: pointer; + margin-left: 8px; +`; diff --git a/src/views/channels/channels/ChannelCard.tsx b/src/views/channels/channels/ChannelCard.tsx index 7592f9bd..8bf37e45 100644 --- a/src/views/channels/channels/ChannelCard.tsx +++ b/src/views/channels/channels/ChannelCard.tsx @@ -1,8 +1,6 @@ import React, { useState } from 'react'; import ReactTooltip from 'react-tooltip'; -import styled from 'styled-components'; import { ArrowDown, ArrowUp, EyeOff } from 'react-feather'; -import { mediaWidths } from 'src/styles/Themes'; import { ChannelType } from 'src/graphql/types'; import { getPercent, formatSeconds } from '../../../utils/helpers'; import { @@ -18,10 +16,7 @@ import { ResponsiveLine, DarkSubTitle, } from '../../../components/generic/Styled'; -import { - useConfigState, - useConfigDispatch, -} from '../../../context/ConfigContext'; +import { useConfigState } from '../../../context/ConfigContext'; import { getStatusDot, getTooltipType, @@ -37,47 +32,14 @@ import { AdminSwitch } from '../../../components/adminSwitch/AdminSwitch'; import { ColorButton } from '../../../components/buttons/colorButton/ColorButton'; import { getPrice } from '../../../components/price/Price'; import { usePriceState } from '../../../context/PriceContext'; - -const IconPadding = styled.div` - display: flex; - flex-direction: column; - margin-left: 8px; - - @media (${mediaWidths.mobile}) { - display: none; - } -`; - -const StatsColumn = styled.div` - width: 100%; - display: flex; - flex-direction: column; - align-items: flex-end; -`; - -const BarSide = styled.div` - width: 50%; - display: flex; - flex-direction: column; - cursor: pointer; - - @media (${mediaWidths.mobile}) { - width: 100%; - } -`; - -const NodeTitle = styled.div` - font-size: 16px; - font-weight: 700; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - - @media (${mediaWidths.mobile}) { - text-align: center; - margin-bottom: 8px; - } -`; +import { + ChannelNodeTitle, + ChannelBarSide, + ChannelIconPadding, + ChannelStatsColumn, + ChannelSingleLine, + ChannelStatsLine, +} from './Channel.style'; const getSymbol = (status: boolean) => { return status ? : ; @@ -115,8 +77,7 @@ export const ChannelCard = ({ biggestBaseFee, biggestRateFee, }: ChannelCardProps) => { - const { channelBarType } = useConfigState(); - const dispatch = useConfigDispatch(); + const { channelBarType, channelBarStyle } = useConfigState(); const [modalOpen, setModalOpen] = useState(false); const { theme, currency, displayValues } = useConfigState(); @@ -180,13 +141,6 @@ export const ChannelCard = ({ } }; - const handleBarClick = () => { - dispatch({ - type: 'change', - channelBarType: channelBarType === 'normal' ? 'partner' : 'normal', - }); - }; - const renderPartner = () => alias ? ( <> @@ -196,8 +150,8 @@ export const ChannelCard = ({ 'Last Update:', `${getDateDif(updated_at)} ago (${getFormatDate(updated_at)})` )} - {renderLine('Base Fee:', `${base_fee} mSats`)} - {renderLine('Fee Rate:', `${fee_rate} sats/MSats`)} + {renderLine('Base Fee:', `${base_fee} msats`)} + {renderLine('Fee Rate:', `${fee_rate} sats/Msats`)} {renderLine('CTLV Delta:', cltv_delta)} ) : ( @@ -253,7 +207,7 @@ export const ChannelCard = ({ switch (channelBarType) { case 'partner': return ( - <> + - + ); - default: + case 'details': return ( - <> + - + + ); + default: + return ( + + + + + + + + + + ); } }; @@ -285,10 +258,11 @@ export const ChannelCard = ({ <>
{`Partner Capacity: ${nodeCapacity}`}
{`Partner Channels: ${channel_count}`}
-
{`Partner Base Fee: ${base_fee} mSats`}
-
{`Partner Fee Rate: ${fee_rate} sats/MSats`}
+
{`Partner Base Fee: ${base_fee} msats`}
+
{`Partner Fee Rate: ${fee_rate} sats/Msats`}
); + case 'details': default: return ( <> @@ -301,34 +275,52 @@ export const ChannelCard = ({ } }; + const getSubCardProps = () => { + switch (channelBarStyle) { + case 'ultracompact': + return { + withMargin: '0 0 4px 0', + padding: index === indexOpen ? '0 0 16px' : '2px 0', + noBackground: true, + }; + case 'compact': + return { + withMargin: '0 0 4px 0', + padding: index === indexOpen ? '4px 8px 16px' : '4px 8px', + }; + default: + return {}; + } + }; + return ( - - - {getStatusDot(is_active, 'active')} - {getStatusDot(is_opening, 'opening')} - {getStatusDot(is_closing, 'closing')} - - - - handleClick()}> + + handleClick()}> + {channelBarStyle === 'normal' && ( + + {getStatusDot(is_active, 'active')} + {getStatusDot(is_opening, 'opening')} + {getStatusDot(is_closing, 'closing')} + + )} + + {alias || partner_public_key?.substring(0, 6)} - {formatBalance} - - - - + {channelBarStyle !== 'ultracompact' && ( + + {formatBalance} + + {getPrivate(is_private)} + {getSymbol(is_partner_initiated)} + + + )} + + {renderBars()} - - - - {getPrivate(is_private)} - {getSymbol(is_partner_initiated)} - - + +
+
{index === indexOpen && renderDetails()} { + const { + channelBarType, + channelBarStyle, + channelSort, + sortDirection, + } = useConfigState(); + const dispatch = useConfigDispatch(); + + const changeStyle = (style: channelBarStyleTypes) => + dispatch({ type: 'change', channelBarStyle: style }); + const changeType = (type: channelBarTypeTypes) => + dispatch({ type: 'change', channelBarType: type }); + const changeSort = (type: channelSortTypes) => + dispatch({ type: 'change', channelSort: type }); + const changeDirection = (type: sortDirectionTypes) => + dispatch({ type: 'change', sortDirection: type }); + + return ( + + + Card Type + + changeStyle('normal')} + > + Normal + + changeStyle('compact')} + > + Compact + + changeStyle('ultracompact')} + > + Ultra-Compact + + + + + Bar Types + + changeType('balance')} + > + Balance + + changeType('details')} + > + Proportional + + changeType('partner')} + > + Partner + + + + + Sort + + changeSort('none')} + > + None + + changeSort('local')} + > + Local + + changeSort('balance')} + > + Balance + + + + {channelSort !== 'none' && ( + + Direction + + changeDirection('increase')} + > + Increasing + + changeDirection('decrease')} + > + Decreasing + + + + )} + + ); +}; diff --git a/src/views/channels/channels/Channels.tsx b/src/views/channels/channels/Channels.tsx index 0a4d5953..023ee0c6 100644 --- a/src/views/channels/channels/Channels.tsx +++ b/src/views/channels/channels/Channels.tsx @@ -2,12 +2,16 @@ import React, { useState } from 'react'; import { toast } from 'react-toastify'; import { useAccountState } from 'src/context/AccountContext'; import { useGetChannelsQuery } from 'src/graphql/queries/__generated__/getChannels.generated'; +import { useConfigState } from 'src/context/ConfigContext'; +import { sortBy } from 'underscore'; +import { getPercent } from 'src/utils/helpers'; import { Card } from '../../../components/generic/Styled'; import { getErrorContent } from '../../../utils/error'; import { LoadingCard } from '../../../components/loading/LoadingCard'; import { ChannelCard } from './ChannelCard'; export const Channels = () => { + const { sortDirection, channelSort } = useConfigState(); const [indexOpen, setIndexOpen] = useState(0); const { auth } = useAccountState(); @@ -62,9 +66,26 @@ export const Channels = () => { } } + const getChannels = () => { + switch (channelSort) { + case 'local': { + const newArray = sortBy(data.getChannels, 'local_balance'); + return sortDirection === 'increase' ? newArray : newArray.reverse(); + } + case 'balance': { + const newArray = sortBy(data.getChannels, channel => + getPercent(channel.local_balance, channel.remote_balance) + ); + return sortDirection === 'increase' ? newArray : newArray.reverse(); + } + default: + return data.getChannels; + } + }; + return ( - {data.getChannels.map((channel, index: number) => ( + {getChannels().map((channel, index: number) => ( { biggest={biggest} biggestPartner={biggestPartner} mostChannels={mostChannels} - biggestBaseFee={biggestBaseFee * 2} + biggestBaseFee={biggestBaseFee} biggestRateFee={biggestRateFee} /> ))}