mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-22 14:22:33 +01:00
chore: add risk checkbox
This commit is contained in:
parent
cf1e579c1d
commit
420cbbfa8f
15 changed files with 233 additions and 85 deletions
1
src/assets/icons/alert-circle.svg
Normal file
1
src/assets/icons/alert-circle.svg
Normal 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-alert-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
|
After Width: | Height: | Size: 356 B |
1
src/assets/icons/check.svg
Normal file
1
src/assets/icons/check.svg
Normal 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-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
After Width: | Height: | Size: 262 B |
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useSpring, animated } from 'react-spring';
|
||||
import { getValue } from 'helpers/Helpers';
|
||||
import { useSettings } from 'context/SettingsContext';
|
||||
import { getValue } from '../../helpers/Helpers';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
|
||||
interface AnimatedProps {
|
||||
amount: number;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import styled from 'styled-components';
|
||||
import { Sub4Title } from '../generic/Styled';
|
||||
import { fontColors, textColor } from 'styles/Themes';
|
||||
|
||||
export const Line = styled.div`
|
||||
margin: 16px 0;
|
||||
|
@ -10,3 +11,25 @@ export const StyledTitle = styled(Sub4Title)`
|
|||
width: 100%;
|
||||
margin-bottom: 0px;
|
||||
`;
|
||||
|
||||
export const CheckboxText = styled.div`
|
||||
font-size: 13px;
|
||||
color: ${fontColors.grey7};
|
||||
text-align: justify;
|
||||
`;
|
||||
|
||||
export const StyledContainer = styled.div`
|
||||
color: ${textColor};
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-right: 32px;
|
||||
margin: 32px 0 8px;
|
||||
`;
|
||||
|
||||
export const FixedWidth = styled.div`
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
margin: 0px;
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
|
|
@ -12,6 +12,7 @@ import { ColorButton } from '../buttons/colorButton/ColorButton';
|
|||
import { Input } from 'components/input/Input';
|
||||
import { Line, StyledTitle } from './Auth.styled';
|
||||
import { ChevronLeft } from 'components/generic/Icons';
|
||||
import { RiskCheckboxAndConfirm } from './Checkboxes';
|
||||
|
||||
interface AuthProps {
|
||||
available: number;
|
||||
|
@ -31,6 +32,7 @@ export const BTCLoginForm = ({
|
|||
|
||||
const [isName, setName] = useState('');
|
||||
const [isJson, setJson] = useState('');
|
||||
const [checked, setChecked] = useState(false);
|
||||
|
||||
const [hasInfo, setHasInfo] = useState(false);
|
||||
const [isPass, setPass] = useState('');
|
||||
|
@ -113,7 +115,7 @@ export const BTCLoginForm = ({
|
|||
};
|
||||
|
||||
const renderContent = () => {
|
||||
const canConnect = isJson !== '' && !!available;
|
||||
const canConnect = isJson !== '' && !!available && checked;
|
||||
return (
|
||||
<>
|
||||
{goBack && (
|
||||
|
@ -129,17 +131,12 @@ export const BTCLoginForm = ({
|
|||
<StyledTitle>BTCPayServer Connect JSON:</StyledTitle>
|
||||
<Input onChange={e => setJson(e.target.value)} />
|
||||
</Line>
|
||||
{canConnect && (
|
||||
<ColorButton
|
||||
<RiskCheckboxAndConfirm
|
||||
disabled={!canConnect}
|
||||
onClick={handleClick}
|
||||
withMargin={'16px 0 0'}
|
||||
fullWidth={true}
|
||||
arrow={true}
|
||||
>
|
||||
Connect
|
||||
</ColorButton>
|
||||
)}
|
||||
handleClick={handleClick}
|
||||
checked={checked}
|
||||
onChange={setChecked}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
55
src/components/auth/Checkboxes.tsx
Normal file
55
src/components/auth/Checkboxes.tsx
Normal file
|
@ -0,0 +1,55 @@
|
|||
import React from 'react';
|
||||
import { Checkbox } from 'components/checkbox/Checkbox';
|
||||
import { CheckboxText, StyledContainer, FixedWidth } from './Auth.styled';
|
||||
import { AlertCircle } from 'components/generic/Icons';
|
||||
import { fontColors } from 'styles/Themes';
|
||||
import { ColorButton } from 'components/buttons/colorButton/ColorButton';
|
||||
|
||||
type CheckboxProps = {
|
||||
handleClick: () => void;
|
||||
disabled: boolean;
|
||||
checked: boolean;
|
||||
onChange: (state: boolean) => void;
|
||||
};
|
||||
|
||||
export const RiskCheckboxAndConfirm = ({
|
||||
handleClick,
|
||||
disabled,
|
||||
checked,
|
||||
onChange,
|
||||
}: CheckboxProps) => (
|
||||
<>
|
||||
<WarningBox />
|
||||
<Checkbox checked={checked} onChange={onChange}>
|
||||
<CheckboxText>
|
||||
I'm feeling reckless - I understand that Lightning, LND and
|
||||
ThunderHub are under constant development and that there is
|
||||
always a risk of losing funds.
|
||||
</CheckboxText>
|
||||
</Checkbox>
|
||||
<ColorButton
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
withMargin={'32px 0 0'}
|
||||
fullWidth={true}
|
||||
arrow={true}
|
||||
>
|
||||
Connect
|
||||
</ColorButton>
|
||||
</>
|
||||
);
|
||||
|
||||
export const WarningBox = () => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<FixedWidth>
|
||||
<AlertCircle color={fontColors.grey7} />
|
||||
</FixedWidth>
|
||||
<CheckboxText>
|
||||
Macaroons are handled by the ThunderHub server to connect to
|
||||
your LND node but are never stored. Still, this involves a
|
||||
certain degree of trust you must be aware of.
|
||||
</CheckboxText>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
|
@ -18,6 +18,7 @@ import { ColorButton } from '../buttons/colorButton/ColorButton';
|
|||
import { Input } from 'components/input/Input';
|
||||
import { Line, StyledTitle } from './Auth.styled';
|
||||
import { ChevronLeft } from 'components/generic/Icons';
|
||||
import { RiskCheckboxAndConfirm } from './Checkboxes';
|
||||
|
||||
interface AuthProps {
|
||||
available: number;
|
||||
|
@ -37,6 +38,7 @@ export const ConnectLoginForm = ({
|
|||
|
||||
const [isName, setName] = useState('');
|
||||
const [isUrl, setUrl] = useState('');
|
||||
const [checked, setChecked] = useState(false);
|
||||
|
||||
const [hasInfo, setHasInfo] = useState(false);
|
||||
const [isPass, setPass] = useState('');
|
||||
|
@ -106,7 +108,7 @@ export const ConnectLoginForm = ({
|
|||
};
|
||||
|
||||
const renderContent = () => {
|
||||
const canConnect = isUrl !== '' && !!available;
|
||||
const canConnect = isUrl !== '' && !!available && checked;
|
||||
return (
|
||||
<>
|
||||
{goBack && (
|
||||
|
@ -122,17 +124,12 @@ export const ConnectLoginForm = ({
|
|||
<StyledTitle>LND Connect Url:</StyledTitle>
|
||||
<Input onChange={e => setUrl(e.target.value)} />
|
||||
</Line>
|
||||
{canConnect && (
|
||||
<ColorButton
|
||||
<RiskCheckboxAndConfirm
|
||||
disabled={!canConnect}
|
||||
onClick={() => setHasInfo(true)}
|
||||
withMargin={'16px 0 0'}
|
||||
fullWidth={true}
|
||||
arrow={true}
|
||||
>
|
||||
Connect
|
||||
</ColorButton>
|
||||
)}
|
||||
handleClick={() => setHasInfo(true)}
|
||||
checked={checked}
|
||||
onChange={setChecked}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
SingleButton,
|
||||
} from 'components/buttons/multiButton/MultiButton';
|
||||
import { ChevronLeft } from 'components/generic/Icons';
|
||||
import { RiskCheckboxAndConfirm } from './Checkboxes';
|
||||
|
||||
interface AuthProps {
|
||||
available: number;
|
||||
|
@ -35,6 +36,7 @@ export const LoginForm = ({
|
|||
const { push } = useHistory();
|
||||
|
||||
const [viewOnly, setViewOnly] = useState(true);
|
||||
const [checked, setChecked] = useState(false);
|
||||
|
||||
const [isName, setName] = useState('');
|
||||
const [isHost, setHost] = useState('');
|
||||
|
@ -131,7 +133,8 @@ export const LoginForm = ({
|
|||
isName !== '' &&
|
||||
isHost !== '' &&
|
||||
(isAdmin !== '' || isRead !== '') &&
|
||||
!!available;
|
||||
!!available &&
|
||||
checked;
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
|
@ -193,17 +196,12 @@ export const LoginForm = ({
|
|||
onChange={e => setCert(e.target.value)}
|
||||
/>
|
||||
</Line>
|
||||
{canConnect && (
|
||||
<ColorButton
|
||||
<RiskCheckboxAndConfirm
|
||||
disabled={!canConnect}
|
||||
onClick={handleClick}
|
||||
withMargin={'16px 0 0'}
|
||||
fullWidth={true}
|
||||
arrow={true}
|
||||
>
|
||||
Connect
|
||||
</ColorButton>
|
||||
)}
|
||||
handleClick={handleClick}
|
||||
checked={checked}
|
||||
onChange={setChecked}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
import React from 'react';
|
||||
import { SingleLine, Sub4Title, SubTitle } from '../generic/Styled';
|
||||
import { Sub4Title, SubTitle } from '../generic/Styled';
|
||||
import zxcvbn from 'zxcvbn';
|
||||
import styled from 'styled-components';
|
||||
import { progressBackground } from '../../styles/Themes';
|
||||
import { Loader } from '../generic/Icons';
|
||||
import { ColorButton } from '../buttons/colorButton/ColorButton';
|
||||
import { Input } from 'components/input/Input';
|
||||
import { Line } from './Auth.styled';
|
||||
|
||||
const Progress = styled.div`
|
||||
width: 80%;
|
||||
margin: 10px 10px 10px 15px;
|
||||
padding: 3px;
|
||||
border-radius: 15px;
|
||||
width: 100%;
|
||||
background: ${progressBackground};
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25),
|
||||
0 1px rgba(255, 255, 255, 0.08);
|
||||
`;
|
||||
|
||||
interface ProgressBar {
|
||||
|
@ -24,12 +19,6 @@ interface ProgressBar {
|
|||
|
||||
const ProgressBar = styled.div`
|
||||
height: 10px;
|
||||
border-radius: 15px;
|
||||
background-image: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0.3),
|
||||
rgba(0, 0, 0, 0.05)
|
||||
);
|
||||
background-color: ${({ barColor }: ProgressBar) =>
|
||||
barColor ? barColor : 'blue'};
|
||||
width: ${({ percent }: ProgressBar) => `${percent}%`};
|
||||
|
@ -66,15 +55,15 @@ export const PasswordInput = ({
|
|||
loading = false,
|
||||
}: PasswordProps) => {
|
||||
const strength = (100 * Math.min(zxcvbn(isPass).guesses_log10, 40)) / 40;
|
||||
const needed = 1;
|
||||
const needed = 20;
|
||||
return (
|
||||
<>
|
||||
<SubTitle>Please Input a Password</SubTitle>
|
||||
<SingleLine>
|
||||
<Line>
|
||||
<Sub4Title>Password:</Sub4Title>
|
||||
<Input onChange={e => setPass(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
</Line>
|
||||
<Line>
|
||||
<Sub4Title>Strength:</Sub4Title>
|
||||
<Progress>
|
||||
<ProgressBar
|
||||
|
@ -82,23 +71,17 @@ export const PasswordInput = ({
|
|||
barColor={getColor(strength)}
|
||||
/>
|
||||
</Progress>
|
||||
</SingleLine>
|
||||
{strength >= needed && !loading && (
|
||||
</Line>
|
||||
<ColorButton
|
||||
disabled={strength < needed}
|
||||
onClick={callback}
|
||||
withMargin={'16px 0 0'}
|
||||
withMargin={'32px 0 0'}
|
||||
fullWidth={true}
|
||||
arrow={true}
|
||||
loading={loading}
|
||||
>
|
||||
Connect
|
||||
</ColorButton>
|
||||
)}
|
||||
{loading && (
|
||||
<ColorButton disabled={true} color={'grey'}>
|
||||
<Loader />
|
||||
</ColorButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
16
src/components/checkbox/Checkbox.stories.tsx
Normal file
16
src/components/checkbox/Checkbox.stories.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Checkbox } from './Checkbox';
|
||||
|
||||
export default {
|
||||
title: 'Checkbox',
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
const [checked, set] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<Checkbox checked={checked} onChange={set}>
|
||||
This is a checkbox
|
||||
</Checkbox>
|
||||
);
|
||||
};
|
58
src/components/checkbox/Checkbox.tsx
Normal file
58
src/components/checkbox/Checkbox.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
colorButtonBackground,
|
||||
buttonBorderColor,
|
||||
themeColors,
|
||||
} from '../../styles/Themes';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-right: 32px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const FixedWidth = styled.div`
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
margin: 0px;
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const StyledCheckbox = styled.div`
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin: 0;
|
||||
border: 1px solid ${buttonBorderColor};
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
transition-duration: 0.3s;
|
||||
background-color: ${colorButtonBackground};
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
|
||||
${({ checked }: { checked: boolean }) =>
|
||||
checked && `background-color: ${themeColors.blue2}`}
|
||||
`;
|
||||
|
||||
type CheckboxProps = {
|
||||
checked: boolean;
|
||||
onChange: (state: boolean) => void;
|
||||
};
|
||||
|
||||
export const Checkbox: React.FC<CheckboxProps> = ({
|
||||
children,
|
||||
checked,
|
||||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<StyledContainer onClick={() => onChange(!checked)}>
|
||||
<FixedWidth>
|
||||
<StyledCheckbox checked={checked} />
|
||||
</FixedWidth>
|
||||
{children}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
|
@ -30,6 +30,7 @@ import { ReactComponent as LayersIcon } from '../../assets/icons/layers.svg';
|
|||
import { ReactComponent as LoaderIcon } from '../../assets/icons/loader.svg';
|
||||
import { ReactComponent as CircleIcon } from '../../assets/icons/circle.svg';
|
||||
import { ReactComponent as AlertTriangleIcon } from '../../assets/icons/alert-triangle.svg';
|
||||
import { ReactComponent as AlertCircleIcon } from '../../assets/icons/alert-circle.svg';
|
||||
import { ReactComponent as GitCommitIcon } from '../../assets/icons/git-commit.svg';
|
||||
import { ReactComponent as GitBranchIcon } from '../../assets/icons/git-branch.svg';
|
||||
import { ReactComponent as RadioIcon } from '../../assets/icons/radio.svg';
|
||||
|
@ -45,6 +46,7 @@ import { ReactComponent as Menu } from '../../assets/icons/menu.svg';
|
|||
import { ReactComponent as Mail } from '../../assets/icons/mail.svg';
|
||||
import { ReactComponent as Github } from '../../assets/icons/github.svg';
|
||||
import { ReactComponent as Repeat } from '../../assets/icons/repeat.svg';
|
||||
import { ReactComponent as CheckIcon } from '../../assets/icons/check.svg';
|
||||
|
||||
interface IconProps {
|
||||
color?: string;
|
||||
|
@ -97,6 +99,7 @@ export const Layers = styleIcon(LayersIcon);
|
|||
export const Loader = styleIcon(LoaderIcon);
|
||||
export const Circle = styleIcon(CircleIcon);
|
||||
export const AlertTriangle = styleIcon(AlertTriangleIcon);
|
||||
export const AlertCircle = styleIcon(AlertCircleIcon);
|
||||
export const GitCommit = styleIcon(GitCommitIcon);
|
||||
export const GitBranch = styleIcon(GitBranchIcon);
|
||||
export const Radio = styleIcon(RadioIcon);
|
||||
|
@ -112,3 +115,4 @@ export const MenuIcon = styleIcon(Menu);
|
|||
export const MailIcon = styleIcon(Mail);
|
||||
export const GithubIcon = styleIcon(Github);
|
||||
export const RepeatIcon = styleIcon(Repeat);
|
||||
export const Check = styleIcon(CheckIcon);
|
||||
|
|
|
@ -68,7 +68,18 @@ export const getAuthParams = (available: string) => {
|
|||
|
||||
export const getAuthLnd = (lndconnect: string) => {
|
||||
const auth = lndconnect.replace('lndconnect', 'https');
|
||||
const url = new URL(auth);
|
||||
|
||||
let url;
|
||||
|
||||
try {
|
||||
url = new URL(auth);
|
||||
} catch (error) {
|
||||
return {
|
||||
cert: '',
|
||||
macaroon: '',
|
||||
socket: '',
|
||||
};
|
||||
}
|
||||
|
||||
const cert = url.searchParams.get('cert') || '';
|
||||
const macaroon = url.searchParams.get('macaroon') || '';
|
||||
|
|
|
@ -24,8 +24,8 @@ export const FaqView = () => {
|
|||
<Section color={themeColors.grey} padding={'60px 0 16px'}>
|
||||
<Question>What is ThunderHub?</Question>
|
||||
<Text>
|
||||
ThunderHub is a <b>LND node manager</b> that you can open on
|
||||
any browser and any device.
|
||||
ThunderHub is a <b>LND node manager</b> that you can open in
|
||||
any browser and on any device.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section color={themeColors.grey} padding={'19px 0 16px'}>
|
||||
|
@ -38,7 +38,7 @@ export const FaqView = () => {
|
|||
<Section color={themeColors.grey} padding={'19px 0 16px'}>
|
||||
<Question>What is the value of ThunderHub?</Question>
|
||||
<Text>
|
||||
ThunderHub brings a <b>full LND lightning node manager </b>
|
||||
ThunderHub brings a <b>full lightning node manager </b>
|
||||
directly to your device without the need of installing
|
||||
plugins, extensions or apps, having specific browsers or
|
||||
operating systems and is completely <b>open-source.</b>
|
||||
|
@ -51,10 +51,7 @@ export const FaqView = () => {
|
|||
with a password only you know.
|
||||
</Text>
|
||||
<Text>
|
||||
<b>
|
||||
No need to trust us, the code is public and available
|
||||
for anyone to audit.
|
||||
</b>
|
||||
<b>The code is public and available for anyone to audit.</b>
|
||||
</Text>
|
||||
</Section>
|
||||
<Section color={themeColors.grey} padding={'19px 0 16px'}>
|
||||
|
@ -72,9 +69,15 @@ export const FaqView = () => {
|
|||
<b> stored only in your browser. </b>
|
||||
The password is only known by you and you need to unlock
|
||||
your account everytime you want to perform an admin only
|
||||
change such as managing channels or sending and recieving
|
||||
change such as managing channels or sending and receiving
|
||||
bitcoin or lightning payments.
|
||||
</Text>
|
||||
<Text>
|
||||
The ThunderHub server uses your credentials to connect to
|
||||
your node but they are never stored outside of your browser.
|
||||
Still, this involves a certain degree of trust you must be
|
||||
aware of.
|
||||
</Text>
|
||||
<Text>
|
||||
If you want a more secure alternative, you can connect using
|
||||
a view-only macaroon and use ThunderHub only for monitoring
|
||||
|
|
|
@ -26,7 +26,7 @@ export const PrivacyView = () => {
|
|||
<Title>Privacy Policy</Title>
|
||||
</Section>
|
||||
<Section color={themeColors.grey} padding={'60px 0 16px'}>
|
||||
<Text>Last Updated: January 30, 2020</Text>
|
||||
<Text>Last Updated: February 12, 2020</Text>
|
||||
</Section>
|
||||
<Section color={themeColors.grey} padding={'19px 0 16px'}>
|
||||
<Text>
|
||||
|
@ -69,8 +69,9 @@ export const PrivacyView = () => {
|
|||
Lightning node, we ask for sensitive information to do so.
|
||||
This information is stored using your browser's own storage
|
||||
APIs, and is encrypted using a password only known to the
|
||||
user. This information is never recorded outside of the
|
||||
user's browser storage.
|
||||
user. This information is used by the server to connect to
|
||||
your node but is never recorded outside of the user's
|
||||
browser storage.
|
||||
</Text>
|
||||
<Text>
|
||||
<b>Error Reporting / Usage Statistics</b> - No information
|
||||
|
|
Loading…
Add table
Reference in a new issue