diff --git a/pages/_app.tsx b/pages/_app.tsx index fd9643dd..b7e5a39a 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -9,47 +9,30 @@ import { Header } from '../src/layouts/header/Header'; import { Footer } from '../src/layouts/footer/Footer'; import { ApolloProvider } from '@apollo/react-hooks'; import withApollo from '../config/apolloClient'; -import { useAccount } from '../src/context/AccountContext'; import { BitcoinFees } from '../src/components/bitcoinInfo/BitcoinFees'; import { BitcoinPrice } from '../src/components/bitcoinInfo/BitcoinPrice'; import { GridWrapper } from '../src/components/gridWrapper/GridWrapper'; import { useRouter } from 'next/router'; -import { - useConnectionState, - useConnectionDispatch, -} from '../src/context/ConnectionContext'; -import { - LoadingView, - ErrorView, -} from '../src/components/stateViews/StateCards'; import { toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import Head from 'next/head'; import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled'; +import { useStatusState } from '../src/context/StatusContext'; -toast.configure({ draggable: false }); - -const withoutGrid = ['/', '/login', '/faq', '/privacy', '/terms']; +toast.configure({ draggable: false, pauseOnFocusLoss: false }); const Wrapper: React.FC = ({ children }) => { - const dispatch = useConnectionDispatch(); const { theme } = useSettings(); - const { loggedIn } = useAccount(); const { pathname } = useRouter(); - const { loading, error } = useConnectionState(); + const { connected } = useStatusState(); - const isInArray = withoutGrid.includes(pathname); + const isRoot = pathname === '/'; const renderContent = () => { - if (error && pathname === '/') { - dispatch({ type: 'disconnected' }); - } - if ((loading || error) && pathname !== '/') { - return ( - {loading ? : } - ); + if (isRoot) { + return <>{children}; } - return {children}; + return {children}; }; const renderGetters = () => ( @@ -60,10 +43,10 @@ const Wrapper: React.FC = ({ children }) => { ); return ( - + - {loggedIn && renderGetters()} + {connected && renderGetters()}
diff --git a/pages/faq.tsx b/pages/faq.tsx deleted file mode 100644 index 41c7eb2a..00000000 --- a/pages/faq.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React from 'react'; -import { Section } from '../src/components/section/Section'; -import { themeColors } from '../src/styles/Themes'; -import { - Title, - Center, - Subtitle, - Question, - Text, -} from '../src/components/typography/Styled'; -import { ContactSection } from '../src/views/homepage/Sections/ContactSection'; -import styled from 'styled-components'; - -const FaqTitle = styled(Title)` - margin-bottom: 0px; -`; - -const FaqView = () => { - return ( - <> -
- FAQ: Answers to Common Questions -
- - Learn about ThunderHub by reading these frequently asked questions. - -
-
-
- What is ThunderHub? - - ThunderHub is a LND node manager that you can open in any - browser and on any device. - -
-
- How much does it cost? - - No cost, ThunderHub is open-source so no need to spend any - precious sats here. - -
-
- What is the value of ThunderHub? - - ThunderHub brings a full lightning node manager - directly to your device without the need of installing plugins, - extensions or apps, having specific browsers or operating systems and - is completely open-source. - - - ThunderHub provides a simple and easy to use manager without - needing to give us any private information. Everything is stored - directly in your browser and sensitive information (like your admin - macaroon) are AES-256 encrypted with a password only you know. - - - The code is public and available for anyone to audit. - -
-
- Is ThunderHub safe to use? - - ThunderHub is open-source and available for anyone to audit. - - This still doesn't mean it's completely bullet proof against - attackers. - - - - If you connect using your admin macaroon, it is - AES-256 encrypted, password protected and - stored only in your browser. - 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 receiving bitcoin or lightning payments. - - - 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. - - - If you want a more secure alternative, you can connect using a - view-only macaroon and use ThunderHub only for monitoring your node. - -
-
- What happens if I forget my password? - - The password is only known by you and is used to unlock an encrypted - macaroon stored only on your browser making it impossible for us to - help you with this situation. - - - The fix is simple. Delete that account on ThunderHub - (Don't worry, this doesn't delete anything or affect in any way your - node) and connect again with your desired macaroons. - -
-
- Am I being tracked in anyway? - - We do not track anything from our users and - store no information on our servers. - Should it be needed in the future, we will notify users and ask for - explicit consent before hand. - -
-
- - Can I use ThunderHub without having my own Lightning Node? - - - ThunderHub provides full lightning node management which is only - possible when you are the owner of the lightning node to which it is - connecting. So in short, - no you cannot use ThunderHub without having your own node. - - - Nowadays with the posibility of using pruned bitcoin nodes (Bitcoin - nodes which only have a section of the Bitcoin blockchain), you can - get a lightning node up and running in the cloud{' '} - for under €15/month. - -
- - - ); -}; - -export default FaqView; diff --git a/pages/index.tsx b/pages/index.tsx index d84ba660..da3451fb 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,23 +1,90 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useAccount } from '../src/context/AccountContext'; import { SessionLogin } from '../src/views/login/SessionLogin'; import { useRouter } from 'next/router'; -import { HomePageView } from '../src/views/homepage/HomePage'; import { appendBasePath } from '../src/utils/basePath'; -import { LoadingView } from '../src/components/loading/LoadingView'; +import { TopSection } from '../src/views/homepage/Top'; +import { LoginBox } from '../src/views/homepage/LoginBox'; +import { Accounts } from '../src/views/homepage/Accounts'; +import { + useStatusState, + useStatusDispatch, +} from '../src/context/StatusContext'; +import { useGetCanConnectLazyQuery } from '../src/generated/graphql'; +import { LoadingCard } from '../src/components/loading/LoadingCard'; +import { Section } from '../src/components/section/Section'; +import { toast } from 'react-toastify'; -const ContextApp: React.FC = () => { +const ContextApp = () => { const { push } = useRouter(); - const { loggedIn, admin, viewOnly, sessionAdmin } = useAccount(); + const { + name, + host, + cert, + admin, + viewOnly, + sessionAdmin, + accounts, + } = useAccount(); + const { loading: statusLoading } = useStatusState(); + const dispatch = useStatusDispatch(); - if (loggedIn) { - if (admin === '' || viewOnly !== '' || sessionAdmin !== '') { + const change = accounts.length <= 1 && admin === ''; + const isSession = admin !== '' && viewOnly === ''; + + const [getCanConnect, { data, loading, error }] = useGetCanConnectLazyQuery({ + fetchPolicy: 'network-only', + onError: () => { + toast.error(`Unable to connect to ${name}`); + dispatch({ type: 'disconnected' }); + }, + }); + + useEffect(() => { + if (loading && !error) { + dispatch({ type: 'loading' }); + } + if (!loading && data?.getNodeInfo && !error) { + dispatch({ type: 'connected' }); push(appendBasePath('/home')); - return ; } - } + }, [loading, data, error]); + + useEffect(() => { + if (viewOnly !== '' || sessionAdmin !== '') { + getCanConnect({ + variables: { + auth: { + host, + macaroon: viewOnly !== '' ? viewOnly : sessionAdmin, + cert, + }, + }, + }); + } + }, [viewOnly, sessionAdmin]); - return !loggedIn && admin === '' ? : ; + return ( + <> + + {statusLoading && ( +
+ +
+ )} + {!statusLoading && ( + <> + {isSession && } + + + + )} + + ); }; export default ContextApp; diff --git a/pages/privacy.tsx b/pages/privacy.tsx deleted file mode 100644 index d4c86332..00000000 --- a/pages/privacy.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import React from 'react'; -import { Section } from '../src/components/section/Section'; -import { themeColors, fontColors } from '../src/styles/Themes'; -import { - Title, - Question, - Text, - BulletPoint, -} from '../src/components/typography/Styled'; -import { ContactSection } from '../src/views/homepage/Sections/ContactSection'; - -import { Link } from '../src/components/link/Link'; - -const PrivacyView = () => { - const props = { color: fontColors.blue2, underline: fontColors.blue2 }; - - const renderLinks = (terms: string, privacy: string) => ( - <> - - Privacy Policy - {' '} - and{' '} - - Terms of Service - - - ); - - return ( - <> -
- Privacy Policy -
-
- Last Updated: February 12, 2020 -
-
- - ThunderHub takes your privacy very seriously, and as a general rule, - will never collect unnecessary data from you without your explicit - consent. However, through using it, you will end up providing us with - some sensitive information, so we want to be explicit about that. - - - This Privacy Policy describes our policies and procedures regarding - our collection and use of your information in connection with your - access and use of{' '} - - https://thunderhub.io - {' '} - (the "Site"). By using this service, you acknowledge and agree to this - Privacy Policy. - - - The terms "us," "we," or "our" refer to ThunderHub. The terms "you", - "your", or "user" refer to those accessing our site. - -
-
- Collection, Storage, and Use of Information - - ThunderHub's site is designed to collect as little information as - possible. The following outlines what is (and is not) collected, and - how it's handled. What we do collect from users is stored either - locally on the user's device, or using browser storage APIs that keep - inaccessible to us without user action. Sensitive information such as - authentication credentials are AES-256 encrypted and stored using a - password known only to the user. - - - Node Credentials - In order to communicate with your 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 used by the - server to connect to your node but is never recorded outside of the - user's browser storage. - - - Error Reporting / Usage Statistics - No information is - recorded. If information must be recorded further on, it will be with - explicit user consent from the user and efforts will be taken to keep - the information anonymous and private. - - - Personal information - Information such as your name, IP - address, email address, or browsing activity are never collected - without explicit consent. While there is no plan to ever ask for - permission to passively collect it, some interactions such as support - requests or bug reports may optionally ask for it. - -
-
- Third Parties - - The following is a list of third parties who may obtain user - information through use of the Site and App, as well as links to their - respective privacy policies. While we aim to only leverage third - parties that we find to be privacy-friendly, and try to keep them as - opt-in as possible, we are not liable for any privacy violations as a - result of the user's interaction with third party services. Any - services found to be exploiting our user's information will be removed - as quickly as possible. - - - Hosting - The following services are used for the hosting and - distrubtion of our site and application. Your information is subject - to their policies when interacting with them. - - - Github - Github's{' '} - {renderLinks( - 'https://help.github.com/en/articles/github-privacy-statement', - 'https://help.github.com/en/articles/github-terms-of-service' - )} - - - AWS - AWS's{' '} - {renderLinks( - 'https://aws.amazon.com/privacy/', - 'https://aws.amazon.com/service-terms/' - )} - - - APIs - For information that your node can't get alone, we link - to or use APIs in our application that leverage the following - services. Your information is subject to their policies when - interacting with them. - - - Earn.com's Bitcoin Fee API - Earn.com's{' '} - {renderLinks( - 'https://earn.com/privacy/', - 'https://earn.com/terms-of-use/' - )} - - - Blockchain Explorer and Price API - Blockchain's{' '} - {renderLinks( - 'https://www.blockchain.com/legal/privacy', - 'https://www.blockchain.com/legal/terms' - )} - -
-
- Law Enforcement Purposes - - If required by a subpoena or court order, we will share information to - law enforcement agencies in order to comply with legal requirements. - We will make reasonable efforts to notify any subjects of such - informational disclosure as is permitted by law. - -
-
- Amendments - - We may alter this Privacy Policy at its discretion at any time for any - or no reason. Any changes will include a corresponding “Last updated” - date shown at the top of this page and such changes will go in effect - immediately. Prominent notice of any changes may be posted on our - site, in our application, or on social media. Such notice is not - guaranteed and you should check for updates regularly. The latest - version of the Privacy Policy will be considered as superseding any - previous version unless otherwise noted. - -
- - - ); -}; - -export default PrivacyView; diff --git a/pages/terms.tsx b/pages/terms.tsx deleted file mode 100644 index 9c916108..00000000 --- a/pages/terms.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React from 'react'; -import { Section } from '../src/components/section/Section'; -import { themeColors, fontColors } from '../src/styles/Themes'; -import { Title, Question, Text } from '../src/components/typography/Styled'; -import { ContactSection } from '../src/views/homepage/Sections/ContactSection'; -import { Link } from '../src/components/link/Link'; - -const TermsView = () => { - const props = { color: fontColors.blue2, underline: fontColors.blue2 }; - - return ( - <> -
- Terms of Service -
-
- Last Updated: January 28, 2020 -
-
- 1. Terms - - ThunderHub ("us", "we", or "our") provides the website{' '} - - https://thunderhub.io - {' '} - (the "Site"). Your use of or access to the site is subject to the - following Terms of Service (the "Agreement"). - -
-
- 2. Disclaimer - - We are not responsible for any loss. LND, the ThunderHub website, and - some of the underlying Javascript libraries we use are under active - development. While every release is tested, there is always the - possibility something unexpected happens that causes your funds to be - lost. Please do not invest more than you are willing to lose. Please - be careful. - -
-
- 3. Limitations - - In no event shall ThunderHub be liable for any damages (including, - without limitation, damages for loss of data or profit, or due to - business interruption) arising out of the use or inability to use the - materials on our website, even if we or an authorized representative - of ours has been notified orally or in writing of the possibility of - such damage. Because some jurisdictions do not allow limitations on - implied warranties, or limitations of liability for consequential or - incidental damages, these limitations may not apply to you. - -
-
- 4. Accuracy of Materials - - The materials appearing on our website could include technical, - typographical, or photographic errors. We do not warrant that any of - the material on the website is accurate, complete or current. We may - make changes to the material contained on the website at any time - without notice. However we do not make any commitment to update the - material. - -
-
- 5. Modifications - - We may revise these terms of service for the website at any time - without notice. By using this website you are agreeing to be bound by - the then current version of these terms of service. - -
-
- 6. Privacy Policy - - Our Privacy Policy describes the way we do and do not collect, use, - store, and disclose your personal information, and is hereby - incorporated by this reference into these Terms. You agree to the - collection, use, storage, and disclosure of your data in accordance - with our{' '} - - Privacy Policy - - . - -
- - - ); -}; - -export default TermsView; diff --git a/src/components/animated/AnimatedNumber.tsx b/src/components/animated/AnimatedNumber.tsx index 2b2e6e7b..a9636bcf 100644 --- a/src/components/animated/AnimatedNumber.tsx +++ b/src/components/animated/AnimatedNumber.tsx @@ -14,7 +14,7 @@ type AnimatedProps = { amount: number; }; -export const AnimatedNumber = ({ amount }: AnimatedProps) => { +export const AnimatedNumber = ({ amount = 0 }: AnimatedProps) => { const { value } = useSpring({ from: { value: 0 }, value: amount, diff --git a/src/components/auth/checks/AdminCheck.tsx b/src/components/auth/checks/AdminCheck.tsx index 2210720c..47d6c498 100644 --- a/src/components/auth/checks/AdminCheck.tsx +++ b/src/components/auth/checks/AdminCheck.tsx @@ -14,6 +14,7 @@ type AdminProps = { export const AdminCheck = ({ host, admin, cert, setChecked }: AdminProps) => { const { data, loading } = useGetCanAdminQuery({ + fetchPolicy: 'network-only', skip: !admin, variables: { auth: { host, macaroon: admin, cert } }, onError: () => { diff --git a/src/components/auth/index.tsx b/src/components/auth/index.tsx index bdea48e9..95663b78 100644 --- a/src/components/auth/index.tsx +++ b/src/components/auth/index.tsx @@ -7,7 +7,6 @@ import { ViewCheck } from './checks/ViewCheck'; import CryptoJS from 'crypto-js'; import { useAccount } from '../../context/AccountContext'; import { saveUserAuth, getAccountId } from '../../utils/auth'; -import { useConnectionDispatch } from '../../context/ConnectionContext'; import { useStatusDispatch } from '../../context/StatusContext'; import { useRouter } from 'next/router'; import { toast } from 'react-toastify'; @@ -35,8 +34,7 @@ export const Auth = ({ type, status, callback, setStatus }: AuthProps) => { const { changeAccount, accounts } = useAccount(); const { push } = useRouter(); - const dispatch = useConnectionDispatch(); - const dispatchState = useStatusDispatch(); + const dispatch = useStatusDispatch(); const [name, setName] = useState(); const [host, setHost] = useState(); @@ -112,7 +110,6 @@ export const Auth = ({ type, status, callback, setStatus }: AuthProps) => { const id = getAccountId(host, viewOnly, admin, cert); dispatch({ type: 'disconnected' }); - dispatchState({ type: 'disconnected' }); changeAccount(id); push(appendBasePath('/')); @@ -141,7 +138,6 @@ export const Auth = ({ type, status, callback, setStatus }: AuthProps) => { const id = getAccountId(host, viewOnly, admin, cert); dispatch({ type: 'disconnected' }); - dispatchState({ type: 'disconnected' }); changeAccount(id); push(appendBasePath('/')); diff --git a/src/components/connectionCheck/ConnectionCheck.tsx b/src/components/connectionCheck/ConnectionCheck.tsx deleted file mode 100644 index 15105c20..00000000 --- a/src/components/connectionCheck/ConnectionCheck.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect } from 'react'; -import { - useConnectionState, - useConnectionDispatch, -} from '../../context/ConnectionContext'; -import { useAccount } from '../../context/AccountContext'; -import { useGetCanConnectQuery } from '../../generated/graphql'; - -export const ConnectionCheck = () => { - const { connected } = useConnectionState(); - const dispatch = useConnectionDispatch(); - - const { auth } = useAccount(); - - const { data, loading } = useGetCanConnectQuery({ - variables: { auth }, - skip: connected, - onError: () => { - dispatch({ type: 'error' }); - }, - }); - - useEffect(() => { - if (!loading && data && data.getNodeInfo) { - dispatch({ type: 'connected' }); - } - }, [data, loading, dispatch]); - - return null; -}; diff --git a/src/components/generic/Styled.tsx b/src/components/generic/Styled.tsx index 3f269f43..25ad491a 100644 --- a/src/components/generic/Styled.tsx +++ b/src/components/generic/Styled.tsx @@ -98,17 +98,19 @@ export const SmallLink = styled.a` type SubTitleProps = { subtitleColor?: string | ThemeSet; fontWeight?: string; + inverseColor?: boolean; }; -export const SubTitle = styled.h4` +export const SubTitle = styled.h4` + color: ${({ inverseColor }) => + inverseColor ? inverseTextColor : textColor}; margin: 5px 0; - ${({ subtitleColor }: SubTitleProps) => + ${({ subtitleColor }) => subtitleColor && css` color: ${subtitleColor}; `} - font-weight: ${({ fontWeight }: SubTitleProps) => - fontWeight ? fontWeight : '500'}; + font-weight: ${({ fontWeight }) => (fontWeight ? fontWeight : '500')}; `; export const InverseSubtitle = styled(SubTitle)` diff --git a/src/components/gridWrapper/GridWrapper.tsx b/src/components/gridWrapper/GridWrapper.tsx index 262e4b96..91105320 100644 --- a/src/components/gridWrapper/GridWrapper.tsx +++ b/src/components/gridWrapper/GridWrapper.tsx @@ -4,7 +4,6 @@ import { mediaWidths } from '../../styles/Themes'; import { Section } from '../section/Section'; import { Navigation } from '../../layouts/navigation/Navigation'; -import { ConnectionCheck } from '../connectionCheck/ConnectionCheck'; import { StatusCheck } from '../statusCheck/StatusCheck'; const Container = styled.div` @@ -23,22 +22,10 @@ const ContentStyle = styled.div` grid-area: content; `; -interface GridWrapperProps { - without?: boolean; -} - -export const GridWrapper: React.FC = ({ - children, - without = false, -}) => { - if (without) { - return <>{children}; - } - +export const GridWrapper: React.FC = ({ children }) => { return (
- {children} diff --git a/src/components/loading/LoadingCard.tsx b/src/components/loading/LoadingCard.tsx index b7d910a0..9ea97402 100644 --- a/src/components/loading/LoadingCard.tsx +++ b/src/components/loading/LoadingCard.tsx @@ -19,6 +19,7 @@ interface LoadingCardProps { color?: string; noTitle?: boolean; loadingHeight?: string; + inverseColor?: boolean; } export const LoadingCard = ({ @@ -27,6 +28,7 @@ export const LoadingCard = ({ noCard = false, noTitle = false, loadingHeight, + inverseColor, }: LoadingCardProps) => { const loadingColor = color ? color : themeColors.blue3; @@ -51,7 +53,7 @@ export const LoadingCard = ({ return ( - {title} + {title} diff --git a/src/components/nodeInfo/NodeInfoModal.tsx b/src/components/nodeInfo/NodeInfoModal.tsx index dd1c3c9e..335ab5d2 100644 --- a/src/components/nodeInfo/NodeInfoModal.tsx +++ b/src/components/nodeInfo/NodeInfoModal.tsx @@ -8,7 +8,6 @@ import { } from '../generic/Styled'; import { Price } from '../price/Price'; import { ColorButton } from '../buttons/colorButton/ColorButton'; -import { useConnectionDispatch } from '../../context/ConnectionContext'; import { useStatusDispatch } from '../../context/StatusContext'; import { useAccount } from '../../context/AccountContext'; @@ -18,8 +17,7 @@ interface NodeInfoModalProps { } export const NodeInfoModal = ({ account, accountId }: NodeInfoModalProps) => { - const dispatch = useConnectionDispatch(); - const dispatchState = useStatusDispatch(); + const dispatch = useStatusDispatch(); const { changeAccount } = useAccount(); @@ -88,8 +86,7 @@ export const NodeInfoModal = ({ account, accountId }: NodeInfoModalProps) => { withMargin={'16px 0 0'} fullWidth={true} onClick={() => { - dispatch({ type: 'disconnected' }); - dispatchState({ + dispatch({ type: 'disconnected', }); changeAccount(accountId); diff --git a/src/components/statusCheck/StatusCheck.tsx b/src/components/statusCheck/StatusCheck.tsx index 11dbe3be..e9235417 100644 --- a/src/components/statusCheck/StatusCheck.tsx +++ b/src/components/statusCheck/StatusCheck.tsx @@ -1,31 +1,29 @@ -import { useConnectionState } from '../../context/ConnectionContext'; import { useAccount } from '../../context/AccountContext'; import { useStatusDispatch } from '../../context/StatusContext'; import { useEffect } from 'react'; -import { toast } from 'react-toastify'; -import { getErrorContent } from '../../utils/error'; import { useGetNodeInfoQuery } from '../../generated/graphql'; +import { useRouter } from 'next/router'; +import { appendBasePath } from '../../utils/basePath'; +import { toast } from 'react-toastify'; export const StatusCheck = () => { - const { connected } = useConnectionState(); const dispatch = useStatusDispatch(); + const { push } = useRouter(); - const { loggedIn, auth } = useAccount(); + const { name, auth } = useAccount(); - const { data, loading, error, stopPolling } = useGetNodeInfoQuery({ + const { data, loading, error } = useGetNodeInfoQuery({ + fetchPolicy: 'network-only', variables: { auth }, - skip: !connected || !loggedIn || !auth, pollInterval: 10000, - onError: error => toast.error(getErrorContent(error)), }); useEffect(() => { - if (!connected || !loggedIn) { - stopPolling(); + if (error) { + toast.error(`Unable to connect to ${name}`); + dispatch({ type: 'disconnected' }); + push(appendBasePath('/')); } - }, [connected, loggedIn, stopPolling]); - - useEffect(() => { if (data && !loading && !error) { const { getChainBalance, @@ -41,7 +39,6 @@ export const StatusCheck = () => { const numbers = onlyVersion[0].split('.'); const state = { - loading: false, alias, syncedToChain: is_synced_to_chain, version: versionNumber[0], diff --git a/src/context/AccountContext.tsx b/src/context/AccountContext.tsx index d6be91b0..87e1b8b5 100644 --- a/src/context/AccountContext.tsx +++ b/src/context/AccountContext.tsx @@ -20,7 +20,6 @@ interface AuthProps { } interface ChangeProps { - loggedIn?: boolean; name?: string; host?: string; admin?: string; @@ -31,7 +30,6 @@ interface ChangeProps { } interface AccountProps { - loggedIn: boolean; name: string; host: string; admin: string; @@ -41,14 +39,12 @@ interface AccountProps { id: string; auth: AuthProps | undefined; accounts: SingleAccountProps[]; - setAccount: (newProps: ChangeProps) => void; changeAccount: (account: string) => void; deleteAccount: (account: string) => void; refreshAccount: () => void; } export const AccountContext = createContext({ - loggedIn: false, name: '', host: '', admin: '', @@ -58,7 +54,6 @@ export const AccountContext = createContext({ id: '', auth: undefined, accounts: [], - setAccount: () => {}, changeAccount: () => {}, deleteAccount: () => {}, refreshAccount: () => {}, @@ -69,32 +64,6 @@ const AccountProvider = ({ children }: any) => { refreshAccount(); }, []); - const setAccount = ({ - loggedIn, - name, - host, - admin, - sessionAdmin, - viewOnly, - cert, - id, - }: ChangeProps) => { - updateAccount((prevState: any) => { - const newState = { ...prevState }; - return merge(newState, { - loggedIn, - name, - host, - admin, - sessionAdmin, - viewOnly, - cert, - id, - auth: getAuthObj(host, viewOnly, sessionAdmin, cert), - }); - }); - }; - const changeAccount = (changeToId: string) => { const currentAccounts = JSON.parse( localStorage.getItem('accounts') || '[]' @@ -145,22 +114,14 @@ const AccountProvider = ({ children }: any) => { const refreshAccount = (account?: string) => { const sessionAdmin = sessionStorage.getItem('session') || ''; - const { - name, - host, - admin, - viewOnly, - cert, - id, - accounts, - loggedIn, - } = getAuth(account); + const { name, host, admin, viewOnly, cert, id, accounts } = getAuth( + account + ); updateAccount((prevState: any) => { const newState = { ...prevState }; const merged = merge(newState, { - loggedIn, name, host, admin, @@ -176,7 +137,6 @@ const AccountProvider = ({ children }: any) => { }; const accountState = { - loggedIn: false, name: '', host: '', admin: '', @@ -186,7 +146,6 @@ const AccountProvider = ({ children }: any) => { id: '', auth: undefined, accounts: [], - setAccount, changeAccount, deleteAccount, refreshAccount, diff --git a/src/context/ConnectionContext.tsx b/src/context/ConnectionContext.tsx deleted file mode 100644 index e97d5162..00000000 --- a/src/context/ConnectionContext.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { createContext, useContext, useReducer } from 'react'; - -type State = { - connected: boolean; - loading: boolean; - error: boolean; -}; - -type ActionType = { - type: 'connected' | 'loading' | 'error' | 'disconnected'; -}; - -type Dispatch = (action: ActionType) => void; - -const StateContext = createContext(undefined); -const DispatchContext = createContext(undefined); - -const stateReducer = (state: State, action: ActionType) => { - switch (action.type) { - case 'connected': - return { connected: true, loading: false, error: false }; - case 'loading': - return { connected: false, loading: true, error: false }; - case 'disconnected': - return { connected: false, loading: false, error: false }; - case 'error': - return { connected: false, loading: false, error: true }; - default: - return { connected: false, loading: false, error: false }; - } -}; - -const initialState = { - connected: false, - loading: false, - error: false, -}; - -const ConnectionProvider = ({ children }: any) => { - const [state, dispatch] = useReducer(stateReducer, initialState); - - return ( - - - {children} - - - ); -}; - -const useConnectionState = () => { - const context = useContext(StateContext); - if (context === undefined) { - throw new Error( - 'useConnectionState must be used within a ConnectionProvider' - ); - } - return context; -}; - -const useConnectionDispatch = () => { - const context = useContext(DispatchContext); - if (context === undefined) { - throw new Error( - 'useConnectionDispatch must be used within a ConnectionProvider' - ); - } - return context; -}; - -export { ConnectionProvider, useConnectionState, useConnectionDispatch }; diff --git a/src/context/ContextProvider.tsx b/src/context/ContextProvider.tsx index 08089cab..7f192948 100644 --- a/src/context/ContextProvider.tsx +++ b/src/context/ContextProvider.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { AccountProvider } from './AccountContext'; import { SettingsProvider } from './SettingsContext'; import { BitcoinInfoProvider } from './BitcoinContext'; -import { ConnectionProvider } from './ConnectionContext'; import { StatusProvider } from './StatusContext'; import { PriceProvider } from './PriceContext'; @@ -11,9 +10,7 @@ export const ContextProvider: React.FC = ({ children }) => ( - - {children} - + {children} diff --git a/src/context/StatusContext.tsx b/src/context/StatusContext.tsx index 23e296d2..7e793137 100644 --- a/src/context/StatusContext.tsx +++ b/src/context/StatusContext.tsx @@ -1,7 +1,11 @@ import React, { createContext, useContext, useReducer } from 'react'; -type State = { +type StateStatus = { loading: boolean; + connected: boolean; +}; + +type State = { alias: string; syncedToChain: boolean; version: string; @@ -14,17 +18,20 @@ type State = { channelPending: number; }; +type CompleteState = State & StateStatus; + type ActionType = { - type: 'connected' | 'disconnected'; + type: 'connected' | 'disconnected' | 'loading'; state?: State; }; type Dispatch = (action: ActionType) => void; -const StateContext = createContext(undefined); +const StateContext = createContext(undefined); const DispatchContext = createContext(undefined); const initialState = { + connected: false, loading: false, alias: '', syncedToChain: false, @@ -38,12 +45,16 @@ const initialState = { channelPending: 0, }; -const stateReducer = (state: State, action: ActionType): State => { +const stateReducer = (state: State, action: ActionType): CompleteState => { switch (action.type) { case 'connected': - return action.state || initialState; + return ( + { ...action.state, loading: false, connected: true } || initialState + ); case 'disconnected': return initialState; + case 'loading': + return { ...initialState, loading: true }; default: return initialState; } diff --git a/src/layouts/footer/Footer.styled.tsx b/src/layouts/footer/Footer.styled.tsx index e0a5d9ec..9ffa0556 100644 --- a/src/layouts/footer/Footer.styled.tsx +++ b/src/layouts/footer/Footer.styled.tsx @@ -1,6 +1,5 @@ import styled from 'styled-components'; import { headerTextColor, fontColors, mediaWidths } from '../../styles/Themes'; -import { HomeButton } from '../../views/homepage/HomePage.styled'; export const FooterWrapper = styled.div` position: absolute; @@ -67,14 +66,6 @@ export const CopyrightText = styled(SideText)` color: ${fontColors.blue}; `; -export const StyledRouter = styled.div` - margin-top: 16px; - - ${HomeButton} { - font-size: 14px; - } -`; - export const Line = styled.div` display: flex; justify-content: center; diff --git a/src/layouts/footer/Footer.tsx b/src/layouts/footer/Footer.tsx index 432bc667..c51bcbfa 100644 --- a/src/layouts/footer/Footer.tsx +++ b/src/layouts/footer/Footer.tsx @@ -2,9 +2,6 @@ import React from 'react'; import { Section } from '../../components/section/Section'; import { Link } from '../../components/link/Link'; import { Emoji } from '../../components/emoji/Emoji'; -import { useAccount } from '../../context/AccountContext'; -import RouterLink from 'next/link'; -import { Zap } from '../../components/generic/Icons'; import getConfig from 'next/config'; import { headerColor, fontColors } from '../../styles/Themes'; import { @@ -17,15 +14,12 @@ import { SideText, CopyrightText, RightFooter, - StyledRouter, } from './Footer.styled'; -import { HomeButton } from '../../views/homepage/HomePage.styled'; const { publicRuntimeConfig } = getConfig(); const { npmVersion } = publicRuntimeConfig; export const Footer = () => { - const { loggedIn } = useAccount(); return (
@@ -50,9 +44,6 @@ export const Footer = () => { - - FAQ - { > Twitter - - Terms of Use - - - Privacy Policy - - {!loggedIn && ( - - - - - LOGIN - - - - )}
diff --git a/src/layouts/header/Header.tsx b/src/layouts/header/Header.tsx index f9994c3b..abf430e1 100644 --- a/src/layouts/header/Header.tsx +++ b/src/layouts/header/Header.tsx @@ -1,15 +1,8 @@ import React, { useState } from 'react'; import { headerColor, headerTextColor } from '../../styles/Themes'; -import { HomeButton } from '../../views/homepage/HomePage.styled'; import { useAccount } from '../../context/AccountContext'; import { SingleLine } from '../../components/generic/Styled'; -import { - Cpu, - MenuIcon, - XSvg, - Zap, - Circle, -} from '../../components/generic/Icons'; +import { Cpu, MenuIcon, XSvg, Circle } from '../../components/generic/Icons'; import { BurgerMenu } from '../../components/burgerMenu/BurgerMenu'; import { useTransition, animated } from 'react-spring'; import { Section } from '../../components/section/Section'; @@ -18,7 +11,6 @@ import { Link } from '../../components/link/Link'; import { ViewSwitch } from '../../components/viewSwitch/ViewSwitch'; import { IconWrapper, - LinkWrapper, HeaderStyle, HeaderLine, HeaderTitle, @@ -29,9 +21,8 @@ const AnimatedBurger = animated(MenuIcon); const AnimatedClose = animated(XSvg); export const Header = () => { - const { loggedIn } = useAccount(); const [open, setOpen] = useState(false); - const { syncedToChain } = useStatusState(); + const { syncedToChain, connected } = useStatusState(); const transitions = useTransition(open, null, { from: { position: 'absolute', opacity: 0 }, @@ -62,41 +53,20 @@ export const Header = () => { ); - const renderLoggedOut = () => ( - <> - - Faq - - - Terms - - - Privacy - - - - - - - - ); - return ( <>
- - - + + + ThunderHub - - {loggedIn ? renderLoggedIn() : renderLoggedOut()} - + {connected && renderLoggedIn()}
diff --git a/src/layouts/navigation/Navigation.tsx b/src/layouts/navigation/Navigation.tsx index 80d692d5..8f483bf7 100644 --- a/src/layouts/navigation/Navigation.tsx +++ b/src/layouts/navigation/Navigation.tsx @@ -24,9 +24,9 @@ import { CreditCard, } from '../../components/generic/Icons'; import { useSettings } from '../../context/SettingsContext'; -import { useConnectionState } from '../../context/ConnectionContext'; import { useRouter } from 'next/router'; import { Link } from '../../components/link/Link'; +import { useStatusState } from '../../context/StatusContext'; const NavigationStyle = styled.div` grid-area: nav; @@ -132,7 +132,7 @@ interface NavigationProps { export const Navigation = ({ isBurger, setOpen }: NavigationProps) => { const { pathname } = useRouter(); const { sidebar, setSettings } = useSettings(); - const { connected } = useConnectionState(); + const { connected } = useStatusState(); const renderNavButton = ( title: string, diff --git a/src/styles/Themes.ts b/src/styles/Themes.ts index d1d04d93..3b2f4b32 100644 --- a/src/styles/Themes.ts +++ b/src/styles/Themes.ts @@ -111,12 +111,17 @@ export const headerTextColor = theme('mode', { export const homeBackgroundColor = theme('mode', { light: themeColors.grey, - dark: themeColors.blue4, + dark: themeColors.grey, }); export const homeBackgroundSecondColor = theme('mode', { light: themeColors.grey2, - dark: themeColors.blue6, + dark: themeColors.grey2, +}); + +export const homeCompatibleColor = theme('mode', { + light: themeColors.blue5, + dark: themeColors.blue5, }); // --------------------------------------- diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 8bc2226e..3de85c77 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -55,7 +55,6 @@ export const getAuth = (account?: string) => { Number(account ?? (localStorage.getItem('active') || '0')), 0 ); - const sessionAdmin = sessionStorage.getItem('session') || ''; const accountsLength = accounts.length; @@ -83,7 +82,6 @@ export const getAuth = (account?: string) => { `${host}-${viewOnly}-${admin !== '' ? 1 : 0}-${cert}`, THUNDERHUB_NAMESPACE ); - const loggedIn = host !== '' && (viewOnly !== '' || sessionAdmin !== ''); return { name, @@ -93,7 +91,6 @@ export const getAuth = (account?: string) => { cert, id: currentId, accounts, - loggedIn, }; }; diff --git a/src/views/home/account/AccountInfo.tsx b/src/views/home/account/AccountInfo.tsx index be26ffd0..7350d757 100644 --- a/src/views/home/account/AccountInfo.tsx +++ b/src/views/home/account/AccountInfo.tsx @@ -52,14 +52,14 @@ export const AccountInfo = () => { const [state, setState] = useState('none'); const { - loading, + connected, chainBalance, chainPending, channelBalance, channelPending, } = useStatusState(); - if (loading) { + if (!connected) { return ( <> diff --git a/src/views/homepage/Accounts.tsx b/src/views/homepage/Accounts.tsx new file mode 100644 index 00000000..460cfade --- /dev/null +++ b/src/views/homepage/Accounts.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; +import { useAccount } from '../../context/AccountContext'; +import { Section } from '../../components/section/Section'; +import { Card, SingleLine } from '../../components/generic/Styled'; +import styled from 'styled-components'; +import { ColorButton } from '../../components/buttons/colorButton/ColorButton'; +import { useGetCanConnectLazyQuery } from '../../generated/graphql'; +import { toast } from 'react-toastify'; +import { ConnectTitle } from './HomePage.styled'; + +const AccountLine = styled.div` + margin: 8px 0; +`; + +const TypeText = styled.div` + font-size: 14px; + margin-right: 8px; +`; + +export const Accounts = ({ change }: { change?: boolean }) => { + const [newAccount, setNewAccount] = React.useState(); + const { id, changeAccount, accounts } = useAccount(); + + const [getCanConnect, { data, loading }] = useGetCanConnectLazyQuery({ + fetchPolicy: 'network-only', + onError: () => { + toast.error('Unable to connect to this node'); + }, + }); + + React.useEffect(() => { + if (!loading && data?.getNodeInfo && newAccount) { + changeAccount(newAccount); + } + }, [data, loading, newAccount]); + + if (accounts.length <= 1) { + return null; + } + + const isType = (admin: string, viewOnly: string): string => { + if (!admin && viewOnly) { + return '(View Only)'; + } + if (admin && !viewOnly) { + return '(Admin Only)'; + } + return null; + }; + + const handleClick = account => () => { + const { id, viewOnly, cert, host } = account; + if (viewOnly) { + setNewAccount(id); + getCanConnect({ + variables: { auth: { host, macaroon: viewOnly, cert } }, + }); + } else { + changeAccount(id); + } + }; + + return ( +
+ + {change ? 'Accounts' : 'Other Accounts'} + + + {accounts.map((account, index) => { + if (account.id === id && !change) { + return; + } + return ( + + + {account.name} + + {isType(account.admin, account.viewOnly)} + + {account.viewOnly ? 'Connect' : 'Login'} + + + + + ); + })} + +
+ ); +}; diff --git a/src/views/homepage/HomePage.styled.ts b/src/views/homepage/HomePage.styled.ts index e93b9c57..f73b7371 100644 --- a/src/views/homepage/HomePage.styled.ts +++ b/src/views/homepage/HomePage.styled.ts @@ -1,26 +1,29 @@ import styled, { keyframes } from 'styled-components'; -import { fontColors, mediaWidths } from '../../styles/Themes'; +import { fontColors, mediaWidths, headerColor } from '../../styles/Themes'; import ThunderHub from '../../assets/ThunderHub.svg'; export const Headline = styled.div` display: flex; align-items: center; - justify-content: space-around; - padding: 48px 0 140px; + justify-content: space-between; + padding: 32px 0 120px; @media (${mediaWidths.mobile}) { - flex-direction: column-reverse; + flex-direction: column; + padding: 0 0 60px; + width: 100%; } `; export const LeftHeadline = styled.div` - width: 50%; + width: 35%; display: flex; flex-direction: column; @media (${mediaWidths.mobile}) { width: 100%; text-align: center; + margin-bottom: 0; } `; @@ -52,38 +55,68 @@ export const StyledImage = styled(ThunderHub)` } @media (${mediaWidths.mobile}) { - width: unset; + width: 100%; } `; -export const HomeButton = styled.button` - cursor: pointer; - outline: none; - padding: 8px 24px; - text-decoration: 2px solid blue; - font-size: 16px; - background-image: linear-gradient(to right, #fd5f00, #f99325); - color: white; - border: none; - display: flex; - align-items: center; - justify-content: center; - border-radius: 8px; - white-space: nowrap; - width: 100%; -`; - -export const Title = styled.h1` +export const HomeTitle = styled.h1` width: 100%; text-align: center; color: ${({ textColor }: { textColor?: string }) => textColor ? textColor : fontColors.white}; - font-size: 32px; - margin-bottom: 0; + font-size: 56px; + margin: 0; + font-weight: 900; + + @media (${mediaWidths.mobile}) { + font-size: 24px; + } `; -export const Text = styled.p` +export const HomeText = styled.p` color: ${fontColors.white}; - text-align: justify; - max-width: 400px; + text-align: center; + font-size: 20px; + + @media (${mediaWidths.mobile}) { + font-size: 15px; + } +`; + +export const SlantedWrapper = styled.div` + position: relative; + width: 100%; + height: 200px; + margin-bottom: -290px; + overflow: hidden; + z-index: -5; +`; + +export const SlantedEdge = styled.div` + content: ''; + width: 100%; + height: 100%; + background: ${headerColor}; + -webkit-transform-origin: 100% 0; + -ms-transform-origin: 100% 0; + transform-origin: 100% 0; + -webkit-transform: skew(84deg); + -ms-transform: skew(84deg); + transform: skew(88deg); + z-index: -5; +`; + +export const FullWidth = styled.div` + display: flex; + justify-content: center; + width: 100%; + margin-top: 8px; +`; + +export const ConnectTitle = styled.div` + width: 100%; + font-size: 18px; + ${({ change }: { change?: boolean }) => + change && `color: ${fontColors.white};`} + padding-bottom: 8px; `; diff --git a/src/views/homepage/HomePage.tsx b/src/views/homepage/HomePage.tsx deleted file mode 100644 index 51765453..00000000 --- a/src/views/homepage/HomePage.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { DetailSection } from './Sections/DetailSection'; -import { ContactSection } from './Sections/ContactSection'; -import { Compatible } from './Sections/Compatible'; -import { InfoSection } from './Sections/InfoSection'; -import { CallToAction } from './Sections/CallToAction'; -import { TopSection } from './Sections/Top'; - -export const HomePageView = () => { - return ( - <> - - - - - - - - ); -}; diff --git a/pages/login.tsx b/src/views/homepage/LoginBox.tsx similarity index 84% rename from pages/login.tsx rename to src/views/homepage/LoginBox.tsx index 70b6b73f..533c6604 100644 --- a/pages/login.tsx +++ b/src/views/homepage/LoginBox.tsx @@ -1,14 +1,15 @@ import React, { useState } from 'react'; -import { Card, Separation } from '../src/components/generic/Styled'; +import { Card, Separation } from '../../components/generic/Styled'; import styled from 'styled-components'; -import { Section } from '../src/components/section/Section'; +import { Section } from '../../components/section/Section'; import { MultiButton, SingleButton, -} from '../src/components/buttons/multiButton/MultiButton'; -import { Link } from '../src/components/link/Link'; -import { Auth } from '../src/components/auth'; -import { mediaWidths, unSelectedNavButton } from '../src/styles/Themes'; +} from '../../components/buttons/multiButton/MultiButton'; +import { Link } from '../../components/link/Link'; +import { Auth } from '../../components/auth'; +import { mediaWidths, unSelectedNavButton } from '../../styles/Themes'; +import { ConnectTitle } from './HomePage.styled'; const Text = styled.p` width: 100%; @@ -36,12 +37,7 @@ const Help = styled.div` } `; -const ConnectTitle = styled.h1` - width: 100%; - text-align: center; -`; - -const LoginView = () => { +export const LoginBox = ({ change }: { change?: boolean }) => { const [isType, setIsType] = useState('login'); const [status, setStatus] = useState('none'); const [help, setHelp] = useState(false); @@ -142,9 +138,11 @@ const LoginView = () => { }; return ( -
- {'How do you want to connect?'} - +
+ + {change ? 'Connect to your Node' : 'Connect to another Node'} + + {status === 'none' && ( <> {renderButtons()} @@ -164,5 +162,3 @@ const LoginView = () => {
); }; - -export default LoginView; diff --git a/src/views/homepage/Sections/CallToAction.tsx b/src/views/homepage/Sections/CallToAction.tsx deleted file mode 100644 index d689f4e3..00000000 --- a/src/views/homepage/Sections/CallToAction.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import { Center } from '../../../components/typography/Styled'; -import { Section } from '../../../components/section/Section'; -import { Padding, StyledH2, StyledH1 } from './Sections.styled'; -import { - homeBackgroundColor, - homeBackgroundSecondColor, -} from '../../../styles/Themes'; -import { HomeButton } from '../HomePage.styled'; -import { Zap } from '../../../components/generic/Icons'; -import styled from 'styled-components'; -import { Link } from '../../../components/link/Link'; - -const CallToActionButton = styled(HomeButton)` - margin: 16px 0 0; -`; - -export const CallToAction = () => { - return ( - <> -
-
- And much more... -
-
-
-
- Ready to take control of your Lightning Node? -
-
- - - - - - Control The Lightning - - -
-
- - ); -}; diff --git a/src/views/homepage/Sections/Compatible.tsx b/src/views/homepage/Sections/Compatible.tsx deleted file mode 100644 index 03e01fe3..00000000 --- a/src/views/homepage/Sections/Compatible.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { Center } from '../../../components/typography/Styled'; -import { Section } from '../../../components/section/Section'; -import { WrapSingleLine, StyledH2, StyledP } from './Sections.styled'; -import { homeBackgroundColor } from '../../../styles/Themes'; - -export const Compatible = () => { - return ( -
-
- Compatible with the latest LND node versions. -
- - v0.7.1-beta - v0.8.0-beta - v0.8.1-beta - v0.8.2-beta - v0.9.0-beta - -
- ); -}; diff --git a/src/views/homepage/Sections/ContactSection.tsx b/src/views/homepage/Sections/ContactSection.tsx deleted file mode 100644 index 12cbbdcd..00000000 --- a/src/views/homepage/Sections/ContactSection.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react'; -import { Section } from '../../../components/section/Section'; -import { fontColors, homeBackgroundColor } from '../../../styles/Themes'; -import { Send, GithubIcon, MailIcon } from '../../../components/generic/Icons'; -import { - Center, - Question, - SectionTitle, - DetailCard, - DetailLine, - SmallText, - IconMargin, - IconTitle, -} from '../../../components/typography/Styled'; -import { Link } from '../../../components/link/Link'; - -export const ContactSection = () => ( -
-
- - Need to contact us? - -
- - - - - - - - - Telegram - - - - Join the chat on Telegram. Usually the quickest reponse. - - - - - - - - - - - Github - - - - See the code, open issues or contribute on github. - - - - - - - - - - - Email - - - Write us an email at thunderhub@protonmail.com - - - -
-); diff --git a/src/views/homepage/Sections/DetailSection.tsx b/src/views/homepage/Sections/DetailSection.tsx deleted file mode 100644 index 2c257a63..00000000 --- a/src/views/homepage/Sections/DetailSection.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React from 'react'; -import { - Card, - SubTitle, - ColumnLine, - Sub4Title, - SingleLine, -} from '../../../components/generic/Styled'; -import styled from 'styled-components'; -import { - Eye, - Send, - Key, - Server, - Sliders, - Users, -} from '../../../components/generic/Icons'; -import { cardColor, mediaWidths } from '../../../styles/Themes'; -import { Section } from '../../../components/section/Section'; - -const Padding = styled.div` - padding: ${({ padding }: { padding?: string }) => - padding ? padding : '16px'}; -`; - -const DetailCard = styled(Card)` - background-color: ${cardColor}; - margin-bottom: 0; - margin: 8px 16px; - z-index: 1; - flex: 1 0 30%; - - @media (${mediaWidths.mobile}) { - flex: 1 0 100%; - } -`; - -const DetailLine = styled.div` - margin: 0 -16px; - display: flex; - justify-content: center; - align-items: center; - flex-wrap: wrap; - @media (${mediaWidths.mobile}) { - margin: 0; - } -`; - -const detailCardContent = (title: string, text: string, Icon: any) => ( - - - - - - - {title} - {text} - - - -); - -export const DetailSection = () => { - return ( -
- - {detailCardContent( - 'Make Payments', - 'Send and receive both Lightning and On-Chain payments.', - Send - )} - {detailCardContent( - 'Multiple Nodes', - 'Connect to multiple nodes and quickly switch between them.', - Server - )} - {detailCardContent( - 'View-Only Mode', - 'Check the status of your node any time without risk.', - Eye - )} - {detailCardContent( - 'AES Encryption', - 'Your Macaroon is AES encrypted with a password only you know.', - Key - )} - {detailCardContent( - 'Open Source', - "Don't trust anyone. Verify the code yourself.", - Users - )} - {detailCardContent( - 'Manage Channels', - 'Open, close and monitor channel status and liquidity', - Sliders - )} - -
- ); -}; diff --git a/src/views/homepage/Sections/InfoSection.tsx b/src/views/homepage/Sections/InfoSection.tsx deleted file mode 100644 index 94b2870e..00000000 --- a/src/views/homepage/Sections/InfoSection.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React from 'react'; -import { Section } from '../../../components/section/Section'; -import { - InfoRow, - ImageSection, - ImagePlace, - TextSection, -} from './Sections.styled'; -import { Text } from '../../../components/typography/Styled'; -import { appendBasePath } from '../../../utils/basePath'; - -export const InfoSection = () => { - return ( - <> -
- - -

Send and Receive

- - Send and receive both Lightning and Bitcoin payments in a simple - and easy to use interface with both basic and advanced features. - -
- - - -
-
-
- - - - - -

Transaction Reports

- - Have a quick overview of the invoices coming into your node and - payments being made. Check both the amount and value of your - transactions. See total amount together with confirmed and - unconfirmed invoices. - -
-
-
-
- - -

Channel Management

- - See all your channels and get a quick preview of how balanced and - active they are. Open new channels or close them. - -
- - - -
-
-
- - - - - -

Forwarded Payments

- - Quick glance at the forwarded payments that have been going - through your node. See the incoming and outgoing channels being - used. - -
-
-
-
- - -

Night/Day Mode

- - Prefer working in the dark? We have an awesome night mode just for - you. Want it more bright? Got you covered as well. - -
- - - -
-
- - ); -}; diff --git a/src/views/homepage/Sections/Sections.styled.tsx b/src/views/homepage/Sections/Sections.styled.tsx deleted file mode 100644 index 31076ebb..00000000 --- a/src/views/homepage/Sections/Sections.styled.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import styled from 'styled-components'; -import { SingleLine } from '../../../components/generic/Styled'; -import { headerColor, mediaWidths } from '../../../styles/Themes'; - -export const Padding = styled.div` - padding: 4px 4px 0 0; -`; - -export const SlantedWrapper = styled.div` - width: 100%; - height: 200px; - margin-bottom: -260px; - overflow: hidden; - z-index: -5; -`; - -export const SlantedEdge = styled.div` - content: ''; - width: 100%; - height: 100%; - background: ${headerColor}; - -webkit-transform-origin: 100% 0; - -ms-transform-origin: 100% 0; - transform-origin: 100% 0; - -webkit-transform: skew(84deg); - -ms-transform: skew(84deg); - transform: skew(88deg); - z-index: -5; -`; - -export const FullWidth = styled.div` - display: flex; - justify-content: center; - width: 100%; - margin-top: 18px; -`; - -export const InfoRow = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - align-items: stretch; - - @media (${mediaWidths.mobile}) { - flex-direction: ${({ reverse }: { reverse?: boolean }) => - reverse ? 'column-reverse' : 'column'}; - } -`; - -export const HalfSection = styled.div` - width: 50%; - display: flex; - flex-direction: column; - - @media (${mediaWidths.mobile}) { - width: 100%; - text-align: center; - } -`; - -export const ImageSection = styled(HalfSection)` - align-self: center; -`; - -export const TextSection = styled(HalfSection)` - padding: 0 32px; - - @media (${mediaWidths.mobile}) { - padding: 0 8px; - } -`; - -export const ImagePlace = styled.img` - display: flex; - width: 100%; - height: auto; - justify-content: center; - align-items: center; -`; - -export const WrapSingleLine = styled(SingleLine)` - flex-wrap: wrap; - justify-content: space-around; - flex-grow: 1; -`; - -export const StyledH1 = styled.h1``; - -export const StyledH2 = styled.h2``; - -export const StyledP = styled.p``; diff --git a/src/views/homepage/Sections/Top.tsx b/src/views/homepage/Sections/Top.tsx deleted file mode 100644 index bec77a74..00000000 --- a/src/views/homepage/Sections/Top.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { - Headline, - LeftHeadline, - StyledImage, - HomeButton, - Title, - Text, -} from '../HomePage.styled'; -import { Zap } from '../../../components/generic/Icons'; -import { headerColor, inverseTextColor } from '../../../styles/Themes'; -import { Section } from '../../../components/section/Section'; -import { - FullWidth, - Padding, - SlantedWrapper, - SlantedEdge, -} from './Sections.styled'; -import { Link } from '../../../components/link/Link'; - -export const TopSection = () => { - return ( - <> -
- - - Control The Power of Lightning - - - Take full control of your lightning node for quick monitoring - and management inside your browser. - - - - - - - - - Control The Lightning - - - - - - -
- - - - - ); -}; diff --git a/src/views/homepage/Top.tsx b/src/views/homepage/Top.tsx new file mode 100644 index 00000000..6c175d5c --- /dev/null +++ b/src/views/homepage/Top.tsx @@ -0,0 +1,60 @@ +import React, { useState } from 'react'; +import { + Headline, + LeftHeadline, + StyledImage, + HomeTitle, + HomeText, + FullWidth, + SlantedWrapper, + SlantedEdge, +} from './HomePage.styled'; +import { headerColor, inverseTextColor } from '../../styles/Themes'; +import { Section } from '../../components/section/Section'; +import { useTransition, animated, config } from 'react-spring'; + +export const TopSection = () => { + const [state] = useState(true); + + const transition = useTransition(state, null, { + config: config.slow, + from: { transform: 'translate3d(-80px,0,0)', opacity: 0 }, + enter: { transform: 'translate3d(0,0,0)', opacity: 1 }, + }); + + const transition2 = useTransition(state, null, { + config: config.slow, + from: { transform: 'translate3d(80px,0,0)', opacity: 0 }, + enter: { transform: 'translate3d(0,0,0)', opacity: 1 }, + }); + + return ( + <> +
+ + + {transition.map(({ props, key }) => ( + + Control the Lightning + + + Monitor and manage your node from any browser and any + device. + + + + ))} + + {transition2.map(({ props, key }) => ( + + + + ))} + +
+ + + + + ); +}; diff --git a/src/views/login/SessionLogin.tsx b/src/views/login/SessionLogin.tsx index a4d90720..38ffdab2 100644 --- a/src/views/login/SessionLogin.tsx +++ b/src/views/login/SessionLogin.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { useAccount } from '../../context/AccountContext'; import { SingleLine, Sub4Title, Card } from '../../components/generic/Styled'; import CryptoJS from 'crypto-js'; @@ -9,31 +9,58 @@ import { Input } from '../../components/input/Input'; import { Section } from '../../components/section/Section'; import { Title } from '../../components/typography/Styled'; import styled from 'styled-components'; -import { textColor } from '../../styles/Themes'; +import { inverseTextColor, mediaWidths } from '../../styles/Themes'; +import { useGetCanConnectLazyQuery } from '../../generated/graphql'; +import { useStatusDispatch } from '../../context/StatusContext'; const StyledTitle = styled(Title)` - font-size: 28px; - color: ${textColor}; + font-size: 24px; + color: ${inverseTextColor}; + + @media (${mediaWidths.mobile}) { + font-size: 18px; + } `; export const SessionLogin = () => { - const { name, admin, refreshAccount } = useAccount(); + const { name, host, admin, cert, refreshAccount } = useAccount(); const [pass, setPass] = useState(''); + const dispatch = useStatusDispatch(); - const handleClick = () => { - try { + const [getCanConnect, { data, loading }] = useGetCanConnectLazyQuery({ + fetchPolicy: 'network-only', + onError: () => { + toast.error('Unable to connect to this node'); + dispatch({ type: 'disconnected' }); + }, + }); + + useEffect(() => { + if (!loading && data?.getNodeInfo) { const bytes = CryptoJS.AES.decrypt(admin, pass); const decrypted = bytes.toString(CryptoJS.enc.Utf8); saveSessionAuth(decrypted); refreshAccount(); + dispatch({ type: 'connected' }); + } + }, [data, loading]); + + const handleClick = () => { + try { + const bytes = CryptoJS.AES.decrypt(admin, pass); + const decrypted = bytes.toString(CryptoJS.enc.Utf8); + + getCanConnect({ + variables: { auth: { host, macaroon: decrypted, cert } }, + }); } catch (error) { toast.error('Wrong Password'); } }; return ( -
+
{`Please Login (${name}):`} @@ -45,11 +72,11 @@ export const SessionLogin = () => { /> Connect diff --git a/src/views/settings/Account.tsx b/src/views/settings/Account.tsx index 3cb29500..34f7804f 100644 --- a/src/views/settings/Account.tsx +++ b/src/views/settings/Account.tsx @@ -15,7 +15,6 @@ import { MultiButton, SingleButton, } from '../../components/buttons/multiButton/MultiButton'; -import { useConnectionDispatch } from '../../context/ConnectionContext'; import { useStatusDispatch } from '../../context/StatusContext'; import { Auth } from '../../components/auth'; import { useRouter } from 'next/router'; @@ -27,8 +26,7 @@ export const AccountSettings = () => { const { push } = useRouter(); const { id, changeAccount, accounts } = useAccount(); - const dispatch = useConnectionDispatch(); - const dispatchState = useStatusDispatch(); + const dispatch = useStatusDispatch(); const [isType, setIsType] = useState('login'); const [willAdd, setWillAdd] = useState(false); @@ -82,9 +80,6 @@ export const AccountSettings = () => { onClick={() => { if (accountId !== id) { dispatch({ type: 'disconnected' }); - dispatchState({ - type: 'disconnected', - }); changeAccount(accountId); push(appendBasePath('/')); } diff --git a/src/views/settings/Danger.tsx b/src/views/settings/Danger.tsx index ca2f7dce..86aa0c2c 100644 --- a/src/views/settings/Danger.tsx +++ b/src/views/settings/Danger.tsx @@ -17,7 +17,6 @@ import { SingleButton, } from '../../components/buttons/multiButton/MultiButton'; import { AlertCircle } from '../../components/generic/Icons'; -import { useConnectionDispatch } from '../../context/ConnectionContext'; import { useStatusDispatch } from '../../context/StatusContext'; import { useRouter } from 'next/router'; import { appendBasePath } from '../../utils/basePath'; @@ -76,8 +75,7 @@ export const DangerView = () => { id, } = useAccount(); - const dispatch = useConnectionDispatch(); - const dispatchState = useStatusDispatch(); + const dispatch = useStatusDispatch(); const { push } = useRouter(); @@ -125,9 +123,6 @@ export const DangerView = () => { const handleDelete = (admin?: boolean) => { deleteAccountPermissions(id, accounts, admin); dispatch({ type: 'disconnected' }); - dispatchState({ - type: 'disconnected', - }); changeAccount(id); push(appendBasePath('/')); };