Skip to content

Commit 81997aa

Browse files
committed
ui+home: add home screen with connect buttons
1 parent 8df1e6d commit 81997aa

File tree

9 files changed

+224
-19
lines changed

9 files changed

+224
-19
lines changed

Diff for: app/src/AppRoutes.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Layout } from 'components/layout';
44

55
const LazyAuthPage = React.lazy(() => import('components/auth/AuthPage'));
66
const LazyLoopPage = React.lazy(() => import('components/loop/LoopPage'));
7+
const LazyHomePage = React.lazy(() => import('components/home/HomePage'));
78
const LazyHistoryPage = React.lazy(() => import('components/history/HistoryPage'));
89
const LazyPoolPage = React.lazy(() => import('components/pool/PoolPage'));
910
const LazySettingsPage = React.lazy(() => import('components/settings/SettingsPage'));
@@ -14,6 +15,14 @@ const AppRoutes: React.FC = () => {
1415
<Routes>
1516
<Route path="/" element={<LazyAuthPage />} />
1617
<Route>
18+
<Route
19+
path="home"
20+
element={
21+
<Layout>
22+
<LazyHomePage />
23+
</Layout>
24+
}
25+
/>
1726
<Route
1827
path="loop"
1928
element={

Diff for: app/src/__tests__/components/layout/Layout.spec.tsx

+12-12
Original file line numberDiff line numberDiff line change
@@ -28,31 +28,31 @@ describe('Layout component', () => {
2828
it('should navigate to the History page', () => {
2929
const { getByText, store } = render();
3030
expect(store.router.location.pathname).toBe('/loop');
31-
fireEvent.click(getByText('History'));
31+
fireEvent.click(getByText('Loop History'));
3232
expect(store.router.location.pathname).toBe('/history');
33-
expect(getByText('History').parentElement).toHaveClass('active');
33+
expect(getByText('Loop History').parentElement).toHaveClass('active');
3434
});
3535

3636
it('should navigate back to the Loop page', () => {
3737
const { getByText, store } = render();
3838
expect(store.router.location.pathname).toBe('/loop');
39-
fireEvent.click(getByText('History'));
39+
fireEvent.click(getByText('Loop History'));
4040
expect(store.router.location.pathname).toBe('/history');
41-
expect(getByText('History').parentElement).toHaveClass('active');
42-
fireEvent.click(getByText('Lightning Loop'));
41+
expect(getByText('Loop History').parentElement).toHaveClass('active');
42+
fireEvent.click(getByText('Loop'));
4343
expect(store.router.location.pathname).toBe('/loop');
44-
expect(getByText('Lightning Loop').parentElement).toHaveClass('active');
44+
expect(getByText('Loop').parentElement).toHaveClass('active');
4545
});
4646

4747
it('should navigate to the Pool page', () => {
4848
const { getByText, store } = render();
4949
expect(store.router.location.pathname).toBe('/loop');
50-
fireEvent.click(getByText('Lightning Pool'));
50+
fireEvent.click(getByText('Pool'));
5151
expect(store.router.location.pathname).toBe('/pool');
52-
expect(getByText('Lightning Pool').parentElement).toHaveClass('active');
53-
fireEvent.click(getByText('Lightning Loop'));
52+
expect(getByText('Pool').parentElement).toHaveClass('active');
53+
fireEvent.click(getByText('Loop'));
5454
expect(store.router.location.pathname).toBe('/loop');
55-
expect(getByText('Lightning Loop').parentElement).toHaveClass('active');
55+
expect(getByText('Loop').parentElement).toHaveClass('active');
5656
});
5757

5858
it('should navigate to the Settings page', () => {
@@ -61,8 +61,8 @@ describe('Layout component', () => {
6161
fireEvent.click(getByText('Settings'));
6262
expect(store.router.location.pathname).toBe('/settings');
6363
expect(getByText('Settings').parentElement).toHaveClass('active');
64-
fireEvent.click(getByText('Lightning Loop'));
64+
fireEvent.click(getByText('Loop'));
6565
expect(store.router.location.pathname).toBe('/loop');
66-
expect(getByText('Lightning Loop').parentElement).toHaveClass('active');
66+
expect(getByText('Loop').parentElement).toHaveClass('active');
6767
});
6868
});

Diff for: app/src/assets/images/home_dash_ss.png

35.4 KB
Loading

Diff for: app/src/assets/images/home_loop_ss.png

43.6 KB
Loading

Diff for: app/src/assets/images/youtube.svg

+4
Loading

Diff for: app/src/components/home/HomePage.tsx

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import React, { useCallback, useState } from 'react';
2+
import { observer } from 'mobx-react-lite';
3+
import styled from '@emotion/styled';
4+
import DashUX from 'assets/images/home_dash_ss.png';
5+
import LoopUX from 'assets/images/home_loop_ss.png';
6+
import { ReactComponent as Youtube } from 'assets/images/youtube.svg';
7+
import { usePrefixedTranslation } from 'hooks';
8+
import { useStore } from 'store';
9+
import {
10+
BoltOutlined,
11+
Button,
12+
Column,
13+
Display,
14+
Paragraph,
15+
QRCode,
16+
Row,
17+
} from 'components/base';
18+
import PurpleButton from 'components/connect/PurpleButton';
19+
import QRCodeModal from 'components/connect/QRCodeModal';
20+
import YoutubeModal from './YoutubeModal';
21+
22+
const Styled = {
23+
Wrapper: styled.div`
24+
padding: 72px 0;
25+
`,
26+
PurpleButton: styled(PurpleButton)`
27+
font-size: ${props => props.theme.sizes.s};
28+
line-height: 24px;
29+
padding: 8px 16px;
30+
margin-right: 24px;
31+
`,
32+
YoutubeButton: styled(Button)`
33+
font-family: ${props => props.theme.fonts.open.semiBold};
34+
padding-left: 0;
35+
36+
svg {
37+
margin-right: 16px;
38+
}
39+
`,
40+
Image: styled.img`
41+
width: 100%;
42+
margin-bottom: 24px;
43+
`,
44+
};
45+
46+
const HomePage: React.FC = () => {
47+
const { l } = usePrefixedTranslation('cmps.home.HomePage');
48+
const [showQR, setShowQR] = useState(false);
49+
const [showVideo, setShowVideo] = useState(false);
50+
const { sessionStore } = useStore();
51+
52+
const toggleQRModal = useCallback(() => setShowQR(v => !v), []);
53+
const toggleVideoModal = useCallback(() => setShowVideo(v => !v), []);
54+
55+
const { Wrapper, PurpleButton, YoutubeButton, Image } = Styled;
56+
return (
57+
<Wrapper>
58+
<Display semiBold space={16}>
59+
{l('pageTitle')}
60+
</Display>
61+
<Paragraph space={32}>{l('connectDesc')}</Paragraph>
62+
<Paragraph space={40}>
63+
<a href={sessionStore.firstSessionTerminalUrl} target="_blank" rel="noreferrer">
64+
<PurpleButton>
65+
<BoltOutlined />
66+
{l('connectTerminalBtn')}
67+
</PurpleButton>
68+
</a>
69+
<PurpleButton secondary onClick={toggleQRModal}>
70+
<QRCode />
71+
{l('connectQrBtn')}
72+
</PurpleButton>
73+
</Paragraph>
74+
<Paragraph space={32}>{l('learnDesc')}</Paragraph>
75+
<Paragraph space={40}>
76+
<YoutubeButton ghost borderless compact onClick={toggleVideoModal}>
77+
<Youtube />
78+
Learn More
79+
</YoutubeButton>
80+
</Paragraph>
81+
<Display semiBold space={16}>
82+
{l('whatsDiff')}
83+
</Display>
84+
<Paragraph space={24}>{l('diffDesc')}</Paragraph>
85+
<Row>
86+
<Column>
87+
<Image src={LoopUX} alt={l('loopTitle')} />
88+
<Paragraph semiBold space={8}>
89+
{l('loopTitle')}
90+
</Paragraph>
91+
<Paragraph muted>{l('loopDesc')}</Paragraph>
92+
</Column>
93+
<Column>
94+
<Image src={DashUX} alt={l('dashTitle')} />
95+
<Paragraph semiBold space={8}>
96+
{l('dashTitle')}
97+
</Paragraph>
98+
<Paragraph muted>{l('dashDesc')}</Paragraph>
99+
</Column>
100+
</Row>
101+
<QRCodeModal
102+
url={sessionStore.firstSessionTerminalUrl}
103+
visible={showQR}
104+
onClose={toggleQRModal}
105+
/>
106+
<YoutubeModal
107+
videoId="5kH1ByxjkTM"
108+
visible={showVideo}
109+
onClose={toggleVideoModal}
110+
/>
111+
</Wrapper>
112+
);
113+
};
114+
115+
export default observer(HomePage);

Diff for: app/src/components/home/YoutubeModal.tsx

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import styled from '@emotion/styled';
3+
import { usePrefixedTranslation } from 'hooks';
4+
import Modal from 'components/common/Modal';
5+
6+
const Styled = {
7+
VideoModal: styled(Modal)`
8+
width: 800px;
9+
max-width: 90%;
10+
overflow: hidden;
11+
`,
12+
VideoWrap: styled.div`
13+
position: relative;
14+
padding-bottom: 56.25%; /* 16:9 */
15+
height: 0;
16+
17+
> iframe {
18+
position: absolute;
19+
top: 0;
20+
left: 0;
21+
width: 100%;
22+
height: 100%;
23+
}
24+
`,
25+
};
26+
27+
interface Props {
28+
videoId: string;
29+
visible: boolean;
30+
onClose: () => void;
31+
}
32+
33+
const YoutubeModal: React.FC<Props> = ({ videoId, visible, onClose }) => {
34+
const { l } = usePrefixedTranslation('cmps.home.YoutubeModal');
35+
const { VideoModal, VideoWrap } = Styled;
36+
return (
37+
<VideoModal title={l('title')} visible={visible} onClose={onClose}>
38+
<VideoWrap>
39+
<iframe
40+
width="568"
41+
height="315"
42+
src={`https://www.youtube.com/embed/${videoId}`}
43+
title="YouTube video player"
44+
frameBorder="0"
45+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
46+
allowFullScreen
47+
></iframe>
48+
</VideoWrap>
49+
</VideoModal>
50+
);
51+
};
52+
53+
export default YoutubeModal;

Diff for: app/src/components/layout/NavMenu.tsx

+11-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { PUBLIC_URL } from '../../config';
88

99
const Styled = {
1010
NavHeader: styled(HeaderFour)`
11-
padding: 8px 14px;
11+
padding: 44px 14px 8px;
12+
font-size: 10px;
13+
line-height: 16px;
1214
`,
1315
Nav: styled.ul`
1416
padding-left: 0;
@@ -78,12 +80,18 @@ const NavMenu: React.FC = () => {
7880
const { NavHeader, Nav } = Styled;
7981
return (
8082
<>
81-
<NavHeader>{l('menu')}</NavHeader>
83+
<Nav>
84+
<NavItem page="home" onClick={appView.goToHome} />
85+
<NavItem page="settings" onClick={appView.goToSettings} />
86+
</Nav>
87+
<NavHeader>{l('liquidityHeader')}</NavHeader>
8288
<Nav>
8389
<NavItem page="loop" onClick={appView.goToLoop} />
8490
<NavItem page="history" onClick={appView.goToHistory} />
8591
<NavItem page="pool" badge={l('common.preview')} onClick={appView.goToPool} />
86-
<NavItem page="settings" onClick={appView.goToSettings} />
92+
</Nav>
93+
<NavHeader>{l('connectHeader')}</NavHeader>
94+
<Nav>
8795
<NavItem page="connect" badge={l('common.beta')} onClick={appView.goToConnect} />
8896
</Nav>
8997
</>

Diff for: app/src/i18n/locales/en-US.json

+20-4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@
5656
"cmps.connect.SessionRow.revoke": "Revoke Session",
5757
"cmps.connect.QRCodeModal.title": "LNC QR",
5858
"cmps.connect.QRCodeModal.desc": "Scan to connect to Terminal from your mobile phone.",
59+
"cmps.home.HomePage.pageTitle": "Home",
60+
"cmps.home.HomePage.connectDesc": "Connect your node to Terminal on the web via the link below. For mobile, generate a QR code to connect.",
61+
"cmps.home.HomePage.connectTerminalBtn": "Connect to Terminal",
62+
"cmps.home.HomePage.connectQrBtn": "Connect with QR",
63+
"cmps.home.HomePage.learnDesc": "The connection to your node occurs through the Lightning Node Connect protocol.",
64+
"cmps.home.HomePage.learnMore": "Learn More",
65+
"cmps.home.HomePage.whatsDiff": "What's different?",
66+
"cmps.home.HomePage.diffDesc": "In the web based Terminal experience, you can expect new features like:",
67+
"cmps.home.HomePage.loopTitle": "Improved Lightning Loop UX",
68+
"cmps.home.HomePage.loopDesc": "Visually focused loop experience with Autoloop built-in.",
69+
"cmps.home.HomePage.dashTitle": "Lightning Terminal Dashboard",
70+
"cmps.home.HomePage.dashDesc": "Easily monitor routing activity and manage your channels through the dashboard.",
71+
"cmps.home.YoutubeModal.title": "Get Connected",
72+
"cmps.home.YoutubeModal.desc": "Get Connected with Lightning Node Connect",
5973
"cmps.history.HistoryPage.backText": "Lightning Loop",
6074
"cmps.history.HistoryPage.pageTitle": "History",
6175
"cmps.history.HistoryRowHeader.status": "Status",
@@ -128,11 +142,13 @@
128142
"cmps.loop.swap.SwapReviewStep.fees": "Fees",
129143
"cmps.loop.swap.SwapReviewStep.total": "Total",
130144
"cmps.loop.swap.SwapWizard.backTip": "Back to Previous",
131-
"cmps.layout.NavMenu.menu": "Menu",
132-
"cmps.layout.NavMenu.loop": "Lightning Loop",
133-
"cmps.layout.NavMenu.history": "History",
134-
"cmps.layout.NavMenu.pool": "Lightning Pool",
145+
"cmps.layout.NavMenu.home": "Home",
135146
"cmps.layout.NavMenu.settings": "Settings",
147+
"cmps.layout.NavMenu.liquidityHeader": "Liquidity",
148+
"cmps.layout.NavMenu.loop": "Loop",
149+
"cmps.layout.NavMenu.history": "Loop History",
150+
"cmps.layout.NavMenu.pool": "Pool",
151+
"cmps.layout.NavMenu.connectHeader": "Connect",
136152
"cmps.layout.NavMenu.connect": "Lightning Node Connect",
137153
"cmps.NodeStatus.title": "Node Status",
138154
"cmps.NodeStatus.offchainTip": "Off-chain Funds",

0 commit comments

Comments
 (0)