feat: adminSwitch and secureButton

This commit is contained in:
Anthony Potdevin 2020-01-03 20:44:17 +05:30
parent be7c4efb3c
commit 22e79529c0
24 changed files with 506 additions and 134 deletions

View file

@ -23,6 +23,7 @@ import { CreateInvoiceCard } from './createInvoice/CreateInvoice';
import { SendOnChainCard } from './sendOnChain/SendOnChain';
import { ReceiveOnChainCard } from './receiveOnChain/ReceiveOnChain';
import { LoadingCard } from '../loading/LoadingCard';
import { AdminSwitch } from '../adminSwitch/AdminSwitch';
const Tile = styled.div`
display: flex;
@ -124,32 +125,34 @@ export const AccountInfo = () => {
<DarkSubTitle>Pending Balance</DarkSubTitle>
<div>{formatPCB}</div>
</Tile>
<ButtonRow>
{showLn && showChain && (
<>
<AdminSwitch>
<ButtonRow>
{showLn && showChain && (
<>
<ColorButton
color={sectionColor}
onClick={() => setState('send_ln')}
>
<Send />
</ColorButton>
<ColorButton
color={sectionColor}
onClick={() => setState('receive_ln')}
>
<DownArrow />
</ColorButton>
</>
)}
{showLn && !showChain && (
<ColorButton
color={sectionColor}
onClick={() => setState('send_ln')}
onClick={() => setState('none')}
>
<Send />
<XSvg />
</ColorButton>
<ColorButton
color={sectionColor}
onClick={() => setState('receive_ln')}
>
<DownArrow />
</ColorButton>
</>
)}
{showLn && !showChain && (
<ColorButton
color={sectionColor}
onClick={() => setState('none')}
>
<XSvg />
</ColorButton>
)}
</ButtonRow>
)}
</ButtonRow>
</AdminSwitch>
</SingleLine>
);
@ -170,32 +173,34 @@ export const AccountInfo = () => {
<DarkSubTitle>Pending Balance</DarkSubTitle>
<div>{formatPB}</div>
</Tile>
<ButtonRow>
{showLn && showChain && (
<>
<AdminSwitch>
<ButtonRow>
{showLn && showChain && (
<>
<ColorButton
color={sectionColor}
onClick={() => setState('send_chain')}
>
<Send />
</ColorButton>
<ColorButton
color={sectionColor}
onClick={() => setState('receive_chain')}
>
<DownArrow />
</ColorButton>
</>
)}
{!showLn && showChain && (
<ColorButton
color={sectionColor}
onClick={() => setState('send_chain')}
onClick={() => setState('none')}
>
<Send />
<XSvg />
</ColorButton>
<ColorButton
color={sectionColor}
onClick={() => setState('receive_chain')}
>
<DownArrow />
</ColorButton>
</>
)}
{!showLn && showChain && (
<ColorButton
color={sectionColor}
onClick={() => setState('none')}
>
<XSvg />
</ColorButton>
)}
</ButtonRow>
)}
</ButtonRow>
</AdminSwitch>
</SingleLine>
);

View file

@ -1,13 +1,12 @@
import React, { useState } from 'react';
import { Input, ColorButton, NoWrapTitle } from '../../generic/Styled';
import { Input, NoWrapTitle } from '../../generic/Styled';
import { useMutation } from '@apollo/react-hooks';
import { CREATE_INVOICE } from '../../../graphql/mutation';
import { Edit } from '../../generic/Icons';
import styled from 'styled-components';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../../utils/error';
import { useAccount } from '../../../context/AccountContext';
import { getAuthString } from '../../../utils/auth';
import { SecureButton } from '../../secureButton/SecureButton';
const SingleLine = styled.div`
display: flex;
@ -18,9 +17,6 @@ const SingleLine = styled.div`
export const CreateInvoiceCard = ({ color }: { color: string }) => {
const [amount, setAmount] = useState(0);
const { host, read, cert } = useAccount();
const auth = getAuthString(host, read, cert);
const [createInvoice, { data, loading }] = useMutation(CREATE_INVOICE, {
onError: error => toast.error(getErrorContent(error)),
});
@ -35,17 +31,16 @@ export const CreateInvoiceCard = ({ color }: { color: string }) => {
type={'number'}
onChange={e => setAmount(parseInt(e.target.value))}
/>
<ColorButton
<SecureButton
callback={createInvoice}
variables={{ amount }}
color={color}
disabled={amount === 0}
enabled={amount > 0}
onClick={() => {
createInvoice({ variables: { amount, auth } });
}}
>
<Edit />
Create Invoice
</ColorButton>
</SecureButton>
</SingleLine>
);
};

View file

@ -1,24 +1,15 @@
import React, { useState } from 'react';
import {
Sub4Title,
Input,
SingleLine,
ColorButton,
} from '../../generic/Styled';
import { Sub4Title, Input, SingleLine } from '../../generic/Styled';
import { useMutation } from '@apollo/react-hooks';
import { PAY_INVOICE } from '../../../graphql/mutation';
import { Send } from '../../generic/Icons';
import { useAccount } from '../../../context/AccountContext';
import { getAuthString } from '../../../utils/auth';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../../utils/error';
import { SecureButton } from '../../secureButton/SecureButton';
export const PayCard = ({ color }: { color: string }) => {
const [request, setRequest] = useState('');
const { host, read, cert } = useAccount();
const auth = getAuthString(host, read, cert);
const [makePayment] = useMutation(PAY_INVOICE, {
onError: error => toast.error(getErrorContent(error)),
onCompleted: () => toast.success('Payment Sent!'),
@ -28,17 +19,16 @@ export const PayCard = ({ color }: { color: string }) => {
<SingleLine>
<Sub4Title>Request:</Sub4Title>
<Input color={color} onChange={e => setRequest(e.target.value)} />
<ColorButton
<SecureButton
callback={makePayment}
variables={{ request }}
color={color}
disabled={request === ''}
enabled={request !== ''}
onClick={() => {
makePayment({ variables: { request, auth } });
}}
disabled={request === ''}
>
<Send />
Send Sats
</ColorButton>
</SecureButton>
</SingleLine>
);
};

View file

@ -11,8 +11,7 @@ import { Edit, Circle } from '../../generic/Icons';
import styled from 'styled-components';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../../utils/error';
import { useAccount } from '../../../context/AccountContext';
import { getAuthString } from '../../../utils/auth';
import { SecureButton } from '../../secureButton/SecureButton';
const SingleLine = styled.div`
display: flex;
@ -37,9 +36,6 @@ export const ReceiveOnChainCard = ({ color }: { color: string }) => {
const [nested, setNested] = useState(false);
const [received, setReceived] = useState(false);
const { host, read, cert } = useAccount();
const auth = getAuthString(host, read, cert);
const [createAddress, { data }] = useMutation(CREATE_ADDRESS, {
onError: error => toast.error(getErrorContent(error)),
});
@ -78,16 +74,16 @@ export const ReceiveOnChainCard = ({ color }: { color: string }) => {
<RadioText>NP2WPKH</RadioText>
</ColorButton>
</ButtonRow>
<ColorButton
<SecureButton
callback={createAddress}
variables={{ nested }}
color={color}
enabled={!received}
disabled={received}
onClick={() => {
createAddress({ variables: { auth, nested } });
}}
>
<Edit />
Create Address
</ColorButton>
</SecureButton>
</SingleLine>
{data && data.createAddress && (
<>

View file

@ -14,11 +14,10 @@ import { Circle, ChevronRight } from '../../generic/Icons';
import styled from 'styled-components';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../../utils/error';
import { useAccount } from '../../../context/AccountContext';
import { getAuthString } from '../../../utils/auth';
import { useSettings } from '../../../context/SettingsContext';
import { getValue } from '../../../helpers/Helpers';
import { useBitcoinInfo } from '../../../context/BitcoinContext';
import { SecureButton } from '../../secureButton/SecureButton';
const RadioText = styled.div`
margin-left: 10px;
@ -35,10 +34,6 @@ const SmallInput = styled(Input)`
max-width: 150px;
`;
const RightButton = styled(ColorButton)`
margin-left: auto;
`;
export const SendOnChainCard = ({ color }: { color: string }) => {
const [address, setAddress] = useState('');
const [tokens, setTokens] = useState(0);
@ -47,13 +42,11 @@ export const SendOnChainCard = ({ color }: { color: string }) => {
const [sendAll, setSendAll] = useState(false);
const [isSent, setIsSent] = useState(false);
const canSend = address !== '' && tokens > 0 && amount > 0;
const { price, symbol, currency } = useSettings();
const { fast, halfHour, hour } = useBitcoinInfo();
const { host, read, cert } = useAccount();
const auth = getAuthString(host, read, cert);
useEffect(() => {
if (type === 'none' && amount === 0) {
setAmount(fast);
@ -210,22 +203,16 @@ export const SendOnChainCard = ({ color }: { color: string }) => {
)}
</SingleLine>
<Separation />
<RightButton
<SecureButton
callback={payAddress}
variables={{ address, ...typeAmount, ...tokenAmount }}
color={color}
onClick={() => {
payAddress({
variables: {
auth,
address,
...typeAmount,
...tokenAmount,
},
});
}}
enabled={canSend}
disabled={!canSend}
>
Send To Address
<ChevronRight />
</RightButton>
</SecureButton>
</>
);
};

View file

@ -0,0 +1,15 @@
interface AdminSwitchProps {
children: any;
}
export const AdminSwitch = ({ children }: AdminSwitchProps) => {
const currentAuth = localStorage.getItem('account') || 'auth1';
const adminMacaroon = localStorage.getItem(`${currentAuth}-admin`) || '';
const sessionAdmin = sessionStorage.getItem('session') || '';
if (!adminMacaroon && !sessionAdmin) {
return null;
}
return children;
};

View file

@ -29,6 +29,7 @@ import { getTransactionLink, getNodeLink } from '../../generic/Helpers';
import Modal from '../../modal/ReactModal';
import { CloseChannel } from '../../closeChannel/CloseChannel';
import styled from 'styled-components';
import { AdminSwitch } from '../../adminSwitch/AdminSwitch';
const CloseButton = styled(ColorButton)`
margin-left: auto;
@ -139,10 +140,15 @@ export const ChannelCard = ({
lastUpdate,
)})`,
)}
<Separation />
<CloseButton color={'red'} onClick={() => setModalOpen(true)}>
Close Channel
</CloseButton>
<AdminSwitch>
<Separation />
<CloseButton
color={'red'}
onClick={() => setModalOpen(true)}
>
Close Channel
</CloseButton>
</AdminSwitch>
</>
);
};

View file

@ -7,6 +7,7 @@ import { useAccount } from '../../../context/AccountContext';
import { getAuthString } from '../../../utils/auth';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../../utils/error';
import { LoadingCard } from '../../loading/LoadingCard';
export const Channels = () => {
const [indexOpen, setIndexOpen] = useState(0);
@ -20,7 +21,7 @@ export const Channels = () => {
});
if (loading || !data || !data.getChannels) {
return <Card>Loading....</Card>;
return <LoadingCard title={'Channels'} />;
}
return (

View file

@ -34,6 +34,7 @@ import { ReactComponent as GitBranchIcon } from '../../icons/git-branch.svg';
import { ReactComponent as RadioIcon } from '../../icons/radio.svg';
import { ReactComponent as CopyIcon } from '../../icons/copy.svg';
import { ReactComponent as ShieldIcon } from '../../icons/shield.svg';
import { ReactComponent as CrosshairIcon } from '../../icons/crosshair.svg';
interface IconProps {
color?: string;
@ -100,3 +101,4 @@ export const GitBranch = styleIcon(GitBranchIcon);
export const Radio = styleIcon(RadioIcon);
export const Copy = styleIcon(CopyIcon);
export const Shield = styleIcon(ShieldIcon);
export const Crosshair = styleIcon(CrosshairIcon);

View file

@ -75,12 +75,6 @@ export const SmallLink = styled.a`
}
`;
export const TitleRow = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`;
export const SubTitle = styled.h4`
margin: 5px 0;
font-weight: 500;

View file

@ -11,6 +11,7 @@ import { XSvg, Layers, GitBranch } from '../generic/Icons';
import { unSelectedNavButton } from '../../styles/Themes';
import { DecodeCard } from './decode/Decode';
import { OpenChannelCard } from './openChannel/OpenChannel';
import { AdminSwitch } from '../adminSwitch/AdminSwitch';
const sectionColor = '#69c0ff';
@ -69,10 +70,14 @@ export const QuickActions = () => {
default:
return (
<QuickRow>
<QuickCard onClick={() => setOpenCard('open_channel')}>
<GitBranch size={'24px'} color={sectionColor} />
<QuickTitle>Open</QuickTitle>
</QuickCard>
<AdminSwitch>
<QuickCard
onClick={() => setOpenCard('open_channel')}
>
<GitBranch size={'24px'} color={sectionColor} />
<QuickTitle>Open</QuickTitle>
</QuickCard>
</AdminSwitch>
<QuickCard onClick={() => setOpenCard('decode')}>
<Layers size={'24px'} color={sectionColor} />
<QuickTitle>Decode</QuickTitle>

View file

@ -9,6 +9,7 @@ import { toast } from 'react-toastify';
import { getErrorContent } from '../../utils/error';
import { PaymentsCard } from './PaymentsCards';
import styled from 'styled-components';
import { LoadingCard } from '../loading/LoadingCard';
export const AddMargin = styled.div`
margin-left: 10px;
@ -33,7 +34,7 @@ export const ResumeList = () => {
}, [data, loading]);
if (loading || !data || !data.getResume) {
return <Card>Loading....</Card>;
return <LoadingCard title={'Resume'} />;
}
const renderInvoices = () => {

View file

@ -0,0 +1,109 @@
import React, { useState } from 'react';
import CryptoJS from 'crypto-js';
import { toast } from 'react-toastify';
import {
SingleLine,
Sub4Title,
Input,
NoWrapTitle,
ColorButton,
} from '../../components/generic/Styled';
import { LoginButton } from '../../components/auth/Password';
import { Circle, ChevronRight } from '../generic/Icons';
import styled from 'styled-components';
import { useAccount } from '../../context/AccountContext';
import { getAuthString } from '../../utils/auth';
const RadioText = styled.div`
margin-left: 10px;
`;
const ButtonRow = styled.div`
width: auto;
display: flex;
justify-content: center;
align-items: center;
`;
interface LoginProps {
macaroon: string;
color: string;
callback: any;
variables: {};
setModalOpen: (value: boolean) => void;
}
export const LoginModal = ({
macaroon,
color,
setModalOpen,
callback,
variables,
}: LoginProps) => {
const [pass, setPass] = useState<string>('');
const [storeSession, setStoreSession] = useState<boolean>(false);
const { host, cert } = useAccount();
const handleClick = () => {
try {
const bytes = CryptoJS.AES.decrypt(macaroon, pass);
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
if (storeSession) {
sessionStorage.setItem('session', decrypted);
}
const auth = getAuthString(host, decrypted, cert);
callback({ variables: { ...variables, auth } });
setModalOpen(false);
} catch (error) {
toast.error('Wrong Password');
}
};
const renderButton = (
onClick: () => void,
text: string,
selected: boolean,
) => (
<ColorButton color={color} onClick={onClick}>
<Circle size={'10px'} fillcolor={selected ? 'white' : ''} />
<RadioText>{text}</RadioText>
</ColorButton>
);
return (
<div>
LOGIN MODAL
<SingleLine>
<Sub4Title>Password:</Sub4Title>
<Input onChange={e => setPass(e.target.value)} />
</SingleLine>
<SingleLine>
<NoWrapTitle>Don't ask me again this session:</NoWrapTitle>
<ButtonRow>
{renderButton(
() => setStoreSession(true),
'Yes',
storeSession,
)}
{renderButton(
() => setStoreSession(false),
'No',
!storeSession,
)}
</ButtonRow>
</SingleLine>
{pass !== '' && (
<LoginButton
disabled={pass === ''}
enabled={pass !== ''}
onClick={handleClick}
color={color}
>
Unlock
<ChevronRight />
</LoginButton>
)}
</div>
);
};

View file

@ -0,0 +1,66 @@
import React, { useState } from 'react';
import Modal from '../modal/ReactModal';
import { LoginModal } from './LoginModal';
import { ColorButton } from '../generic/Styled';
import { useAccount } from '../../context/AccountContext';
import { getAuthString } from '../../utils/auth';
interface SecureButtonProps {
callback: any;
color: string;
disabled: boolean;
enabled: boolean;
children: any;
variables: {};
}
export const SecureButton = ({
callback,
color,
enabled,
disabled,
children,
variables,
}: SecureButtonProps) => {
const [modalOpen, setModalOpen] = useState<boolean>(false);
const { host, cert } = useAccount();
const currentAuth = localStorage.getItem('account') || 'auth1';
const adminMacaroon = localStorage.getItem(`${currentAuth}-admin`) || '';
const sessionAdmin = sessionStorage.getItem('session') || '';
if (!adminMacaroon && !sessionAdmin) {
return null;
}
const auth = getAuthString(host, sessionAdmin, cert);
const handleClick = () => setModalOpen(true);
const onClick = sessionAdmin
? () => callback({ variables: { ...variables, auth } })
: handleClick;
return (
<>
<ColorButton
color={color}
disabled={disabled}
enabled={enabled}
onClick={onClick}
>
{children}
</ColorButton>
<Modal isOpen={modalOpen} setIsOpen={setModalOpen}>
<LoginModal
color={color}
macaroon={adminMacaroon}
setModalOpen={setModalOpen}
callback={callback}
variables={variables}
/>
</Modal>
</>
);
};

View file

@ -93,8 +93,6 @@ const AccountProvider = ({ children }: any) => {
const readMacaroon = read ? read : sessionAdmin;
const loggedIn = host !== '' && readMacaroon !== '';
console.log('REFRESHING', readMacaroon);
updateAccount((prevState: any) => {
const newState = { ...prevState };
return merge(newState, {

View file

@ -216,3 +216,16 @@ export const RECOVER_FUNDS = gql`
recoverFunds(auth: $auth, backup: $backup)
}
`;
export const CHANNEL_FEES = gql`
query GetChannelFees($auth: String!) {
getChannelFees(auth: $auth) {
alias
color
baseFee
feeRate
transactionId
transactionVout
}
}
`;

1
src/icons/crosshair.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-crosshair"><circle cx="12" cy="12" r="10"></circle><line x1="22" y1="12" x2="18" y2="12"></line><line x1="6" y1="12" x2="2" y2="12"></line><line x1="12" y1="6" x2="12" y2="2"></line><line x1="12" y1="22" x2="12" y2="18"></line></svg>

After

Width:  |  Height:  |  Size: 437 B

View file

@ -7,6 +7,7 @@ import { ChannelView } from '../../views/channels/ChannelView';
import { SettingsView } from '../../views/settings/Settings';
import { ResumeList } from '../../components/resume/ResumeList';
import { BackupsView } from '../../views/backups/Backups';
import { FeesView } from '../../views/fees/Fees';
const ContentStyle = styled.div`
/* display: flex;
@ -27,6 +28,7 @@ export const Content = () => {
<Route path="/backups" render={() => <BackupsView />} />
<Route path="/resume" render={() => <ResumeList />} />
<Route path="/settings" render={() => <SettingsView />} />
<Route path="/fees" render={() => <FeesView />} />
<Route path="*" render={() => <NotFound />} />
</Switch>
</ContentStyle>

View file

@ -14,6 +14,7 @@ import {
Server,
Settings,
Shield,
Crosshair,
} from '../../components/generic/Icons';
import { useSettings } from '../../context/SettingsContext';
@ -84,6 +85,7 @@ const CHANNEL_LINK = '/channels';
const RESUME_LINK = '/resume';
const BACKUPS_LINK = '/backups';
const SETTINGS_LINK = '/settings';
const FEES_LINK = '/fees';
export const Navigation = () => {
const { theme } = useSettings();
@ -113,6 +115,15 @@ export const Navigation = () => {
<NavSeparation />
Channels
</NavButton>
<NavButton
selectedColor={navButtonColor[theme]}
selected={pathname === FEES_LINK}
to={FEES_LINK}
>
<Crosshair />
<NavSeparation />
Fees
</NavButton>
<NavButton
selectedColor={navButtonColor[theme]}
selected={pathname === RESUME_LINK}

View file

@ -11,6 +11,7 @@ import {
import { DownloadBackups } from './DownloadBackups';
import { VerifyBackups } from './VerifyBackups';
import { RecoverFunds } from './RecoverFunds';
import { AdminSwitch } from '../../components/adminSwitch/AdminSwitch';
const backupColor = '#ffffff';
@ -37,7 +38,9 @@ export const BackupsView = () => {
<Separation />
<DownloadBackups color={backupColor} />
<VerifyBackups color={backupColor} />
<RecoverFunds color={backupColor} />
<AdminSwitch>
<RecoverFunds color={backupColor} />
</AdminSwitch>
</Card>
</CardWithTitle>
);

View file

@ -14,11 +14,7 @@ import {
} from '../../components/generic/Styled';
import ScaleLoader from 'react-spinners/ScaleLoader';
import { XSvg, ChevronRight } from '../../components/generic/Icons';
import styled from 'styled-components';
const RightButton = styled(ColorButton)`
margin: 0 0 0 auto;
`;
import { SecureButton } from '../../components/secureButton/SecureButton';
export const RecoverFunds = ({ color }: { color: string }) => {
const [backupString, setBackupString] = useState<string>('');
@ -43,12 +39,12 @@ export const RecoverFunds = ({ color }: { color: string }) => {
<DarkSubTitle>Backup String: </DarkSubTitle>
<Input onChange={e => setBackupString(e.target.value)} />
</SingleLine>
<RightButton
disabled={backupString === ''}
<SecureButton
callback={recoverFunds}
variables={{ backup: backupString }}
color={color}
onClick={() =>
recoverFunds({ variables: { auth, backup: backupString } })
}
enabled={true}
disabled={backupString === ''}
>
{loading ? (
<ScaleLoader height={8} width={2} color={color} />
@ -58,7 +54,7 @@ export const RecoverFunds = ({ color }: { color: string }) => {
<ChevronRight />
</>
)}
</RightButton>
</SecureButton>
</SubCard>
);

View file

@ -0,0 +1,83 @@
import React from 'react';
import {
SubCard,
Separation,
SingleLine,
DarkSubTitle,
} from '../../components/generic/Styled';
import { renderLine } from '../../components/generic/Helpers';
import { MainInfo, NodeTitle, ColLine } from './Fees.style';
interface FeeCardProps {
channelInfo: any;
index: number;
setIndexOpen: (index: number) => void;
indexOpen: number;
}
export const FeeCard = ({
channelInfo,
index,
setIndexOpen,
indexOpen,
}: FeeCardProps) => {
const {
alias,
color,
baseFee,
feeRate,
transactionId,
transactionVout,
} = channelInfo;
const handleClick = () => {
if (indexOpen === index) {
setIndexOpen(0);
} else {
setIndexOpen(index);
}
};
const renderDetails = () => {
return (
<>
<Separation />
{renderLine('Transaction Id:', transactionId)}
{renderLine('Transaction Vout:', transactionVout)}
</>
);
};
return (
<SubCard color={color} key={index}>
<MainInfo onClick={() => handleClick()}>
<SingleLine>
<NodeTitle>{alias ? alias : 'Unknown'}</NodeTitle>
<ColLine>
<SingleLine>
<DarkSubTitle>{`Base Fee:`}</DarkSubTitle>
<SingleLine>
{baseFee}
<DarkSubTitle>
{baseFee === 1 ? 'sat' : 'sats'}
</DarkSubTitle>
</SingleLine>
</SingleLine>
<SingleLine>
<DarkSubTitle>{`Fee Rate:`}</DarkSubTitle>
<SingleLine>
{feeRate}
<DarkSubTitle>
{feeRate === 1
? 'sat/million'
: 'sats/million'}
</DarkSubTitle>
</SingleLine>
</SingleLine>
</ColLine>
</SingleLine>
</MainInfo>
{index === indexOpen && renderDetails()}
</SubCard>
);
};

View file

@ -0,0 +1,50 @@
import styled from 'styled-components';
export const ColLine = styled.div`
display: flex;
flex-direction: column;
/* justify-content: space-between; */
/* flex-grow: 2; */
flex-basis: 30%;
`;
export const NodeTitle = styled.div`
/* padding: 2px; */
font-size: 16px;
font-weight: bold;
`;
export const StatusLine = styled.div`
width: 100%;
position: relative;
right: -8px;
top: -8px;
display: flex;
/* flex-direction: column; */
justify-content: flex-end;
/* align-items: flex-start; */
/* z-index: 2; */
margin: 0 0 -8px 0;
/* height: 36px; */
/* margin-left: 5px; */
/* margin: -8px -7px 0 0; */
`;
export const StatusDot = styled.div`
margin: 0 2px;
height: 8px;
width: 8px;
border-radius: 100%;
background-color: ${({ color }: { color: string }) => color};
`;
export const DetailLine = styled.div`
font-size: 14px;
word-wrap: break-word;
display: flex;
justify-content: space-between;
`;
export const MainInfo = styled.div`
cursor: pointer;
`;

43
src/views/fees/Fees.tsx Normal file
View file

@ -0,0 +1,43 @@
import React, { useState } from 'react';
import { useQuery } from '@apollo/react-hooks';
import { CHANNEL_FEES } from '../../graphql/query';
import { Card, CardWithTitle, SubTitle } from '../../components/generic/Styled';
import { useAccount } from '../../context/AccountContext';
import { getAuthString } from '../../utils/auth';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../utils/error';
import { LoadingCard } from '../../components/loading/LoadingCard';
import { FeeCard } from './FeeCard';
export const FeesView = () => {
const [indexOpen, setIndexOpen] = useState(0);
const { host, read, cert } = useAccount();
const auth = getAuthString(host, read, cert);
const { loading, data } = useQuery(CHANNEL_FEES, {
variables: { auth },
onError: error => toast.error(getErrorContent(error)),
});
if (loading || !data || !data.getChannelFees) {
return <LoadingCard title={'Fees'} />;
}
return (
<CardWithTitle>
<SubTitle>Fees</SubTitle>
<Card>
{data.getChannelFees.map((channel: any, index: number) => (
<FeeCard
channelInfo={channel}
index={index + 1}
setIndexOpen={setIndexOpen}
indexOpen={indexOpen}
key={index}
/>
))}
</Card>
</CardWithTitle>
);
};