Skip to content

Commit cfcacbe

Browse files
committed
connect: improve connect UX
1 parent c132f60 commit cfcacbe

File tree

11 files changed

+192
-56
lines changed

11 files changed

+192
-56
lines changed
Lines changed: 6 additions & 0 deletions
Loading

app/src/components/common/FormField.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const Styled = {
1818
};
1919

2020
interface Props {
21-
label: string;
21+
label?: string;
2222
info?: ReactNode;
2323
error?: ReactNode;
2424
tip?: string;
@@ -28,14 +28,16 @@ const FormField: React.FC<Props> = ({ label, info, error, tip, children }) => {
2828
const { Wrapper, Info } = Styled;
2929
return (
3030
<Wrapper>
31-
<HeaderFour>
32-
{label}
33-
{tip && (
34-
<Tip overlay={tip} placement="right" capitalize={false}>
35-
<HelpCircle size="medium" />
36-
</Tip>
37-
)}
38-
</HeaderFour>
31+
{label && (
32+
<HeaderFour>
33+
{label}
34+
{tip && (
35+
<Tip overlay={tip} placement="right" capitalize={false}>
36+
<HelpCircle size="medium" />
37+
</Tip>
38+
)}
39+
</HeaderFour>
40+
)}
3941
{children}
4042
<Info error={!!error}>{error || info}</Info>
4143
</Wrapper>

app/src/components/connect/AddSession.tsx

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,30 @@ import styled from '@emotion/styled';
44
import { usePrefixedTranslation } from 'hooks';
55
import { MAX_DATE } from 'util/constants';
66
import { useStore } from 'store';
7-
import { Button, Column, Row } from 'components/base';
7+
import { Button, Column, HeaderFour, Row } from 'components/base';
88
import FormField from 'components/common/FormField';
99
import FormInput from 'components/common/FormInput';
10+
import PurpleButton from './PurpleButton';
1011

1112
const Styled = {
1213
Wrapper: styled.div``,
13-
AddSection: styled.div`
14-
text-align: center;
14+
FormHeader: styled(HeaderFour)`
15+
color: ${props => props.theme.colors.white};
1516
`,
16-
ActionColumn: styled(Column)`
17-
padding-top: 20px;
17+
FormInput: styled(FormInput)`
18+
> input {
19+
font-family: ${props => props.theme.fonts.open.regular};
20+
font-size: ${props => props.theme.sizes.m};
21+
padding: 12px 16px;
22+
}
1823
`,
1924
};
2025

21-
const AddSession: React.FC = () => {
26+
interface Props {
27+
primary?: boolean;
28+
}
29+
30+
const AddSession: React.FC<Props> = ({ primary }) => {
2231
const { l } = usePrefixedTranslation('cmps.connect.AddSession');
2332
const { sessionStore } = useStore();
2433

@@ -34,31 +43,30 @@ const AddSession: React.FC = () => {
3443
}
3544
}, [label]);
3645

37-
const { Wrapper, AddSection, ActionColumn } = Styled;
46+
const { Wrapper, FormHeader, FormInput } = Styled;
3847
if (!editing) {
3948
return (
40-
<AddSection>
41-
<Button onClick={toggleEditing}>{l('create')}</Button>
42-
</AddSection>
49+
<PurpleButton tertiary={!primary} onClick={toggleEditing}>
50+
{l('create')}
51+
</PurpleButton>
4352
);
4453
}
4554

4655
return (
4756
<Wrapper>
57+
<FormHeader>{l('label')}</FormHeader>
4858
<Row>
49-
<Column cols={6} className="offset-1">
50-
<FormField label={l('label')}>
59+
<Column cols={6}>
60+
<FormField>
5161
<FormInput value={label} onChange={setLabel} placeholder={l('labelHint')} />
5262
</FormField>
5363
</Column>
54-
<ActionColumn className="">
55-
<Button primary onClick={handleSubmit}>
56-
{l('common.submit')}
57-
</Button>
64+
<Column>
65+
<PurpleButton onClick={handleSubmit}>{l('common.submit')}</PurpleButton>
5866
<Button ghost borderless onClick={toggleEditing}>
5967
{l('common.cancel')}
6068
</Button>
61-
</ActionColumn>
69+
</Column>
6270
</Row>
6371
</Wrapper>
6472
);

app/src/components/connect/ConnectPage.tsx

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,63 @@
11
import React from 'react';
22
import { observer } from 'mobx-react-lite';
33
import styled from '@emotion/styled';
4+
import nodeConnectSvg from 'assets/images/lightning-node-connect.svg';
45
import { usePrefixedTranslation } from 'hooks';
5-
import PageHeader from 'components/common/PageHeader';
6+
import { useStore } from 'store';
7+
import { Copy } from 'components/base';
68
import AddSession from './AddSession';
9+
import PurpleButton from './PurpleButton';
710
import SessionList from './SessionList';
811

912
const Styled = {
1013
Wrapper: styled.section`
11-
padding: 40px 0;
14+
padding: 80px 0 0 80px;
15+
`,
16+
DisplayLarge: styled.div`
17+
font-family: ${props => props.theme.fonts.open.semiBold};
18+
font-size: 32px;
19+
line-height: 40px;
20+
margin-top: 32px;
21+
margin-bottom: 16px;
1222
`,
1323
Description: styled.div`
14-
margin: 60px;
24+
margin-bottom: 32px;
1525
`,
16-
Info: styled.div`
17-
margin: 100px 60px;
18-
color: ${props => props.theme.colors.gray};
19-
text-align: center;
20-
font-size: ${props => props.theme.sizes.s};
26+
Divider: styled.div`
27+
max-width: 640px;
28+
border: 1px solid #384770;
29+
margin: 32px 0;
2130
`,
2231
};
2332

2433
const ConnectPage: React.FC = () => {
2534
const { l } = usePrefixedTranslation('cmps.connect.ConnectPage');
35+
const { sessionStore } = useStore();
2636

27-
const { Wrapper, Description, Info } = Styled;
28-
return (
37+
const { Wrapper, DisplayLarge, Description, Divider } = Styled;
38+
return !sessionStore.hasMultiple ? (
2939
<Wrapper>
30-
<PageHeader title={l('pageTitle')} />
31-
<Description>{l('description')}</Description>
40+
<img src={nodeConnectSvg} alt={l('pageTitle')} />
41+
<DisplayLarge>{l('pageTitle')}</DisplayLarge>
42+
<Description>
43+
{l('description1')}
44+
<br />
45+
{l('description2')}
46+
</Description>
47+
<PurpleButton onClick={sessionStore.copyFirstPhrase}>
48+
<Copy />
49+
{l('copyPhraseLabel')}
50+
</PurpleButton>
51+
<Divider />
52+
<Description>{l('addlDesc')}</Description>
3253
<AddSession />
54+
</Wrapper>
55+
) : (
56+
<Wrapper>
57+
<DisplayLarge>{l('pageTitle')}</DisplayLarge>
58+
<Description>{l('description1')}</Description>
59+
<AddSession primary />
3360
<SessionList />
34-
<Info>{l('info')}</Info>
3561
</Wrapper>
3662
);
3763
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import styled from '@emotion/styled';
2+
3+
interface Props {
4+
tertiary?: boolean;
5+
}
6+
7+
const PurpleButton = styled.button<Props>`
8+
font-family: ${props => props.theme.fonts.open.regular};
9+
font-size: ${props => props.theme.sizes.m};
10+
font-weight: 600;
11+
height: auto;
12+
color: white;
13+
background-color: #5d5fef;
14+
border-width: 0;
15+
border-radius: 4px;
16+
padding: 14px 24px;
17+
18+
&:hover {
19+
background-color: #3d40e7;
20+
}
21+
22+
&:focus {
23+
outline: none;
24+
}
25+
26+
${props =>
27+
props.tertiary &&
28+
`
29+
background-color: #384770;
30+
31+
&:hover {
32+
background-color: #2E3A5C;
33+
}
34+
`}
35+
36+
> svg {
37+
margin-right: 6px;
38+
}
39+
`;
40+
41+
export default PurpleButton;

app/src/components/connect/SessionList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import SessionRow, { ROW_HEIGHT, SessionRowHeader } from './SessionRow';
88

99
const Styled = {
1010
Wrapper: styled.div`
11-
margin: 100px 0;
11+
margin: 40px 0;
1212
`,
1313
};
1414

app/src/components/connect/SessionRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const SessionRow: React.FC<Props> = ({ session, style }) => {
103103
const { sessionStore } = useStore();
104104

105105
const handleCopy = useCallback(() => {
106-
sessionStore.copyPhrase(session.pairingSecretMnemonic);
106+
sessionStore.copyPhrase(session.label, session.pairingSecretMnemonic);
107107
}, [session.pairingSecretMnemonic]);
108108

109109
const handleRevoke = useCallback(() => {

app/src/components/layout/NavMenu.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ const Styled = {
4848

4949
const NavItem: React.FC<{
5050
page: string;
51-
preview?: boolean;
51+
badge?: string;
5252
onClick: () => void;
53-
}> = observer(({ page, preview, onClick }) => {
53+
}> = observer(({ page, badge, onClick }) => {
5454
const { l } = usePrefixedTranslation('cmps.layout.NavMenu');
5555
const { router } = useStore();
5656
const className = router.location.pathname.startsWith(`${PUBLIC_URL}/${page}`)
@@ -61,9 +61,9 @@ const NavItem: React.FC<{
6161
<Styled.NavItem className={className}>
6262
<span onClick={onClick}>
6363
{l(page)}
64-
{preview && (
64+
{badge && (
6565
<sup>
66-
<Badge muted>{l('common.preview')}</Badge>
66+
<Badge muted>{badge}</Badge>
6767
</sup>
6868
)}
6969
</span>
@@ -82,9 +82,9 @@ const NavMenu: React.FC = () => {
8282
<Nav>
8383
<NavItem page="loop" onClick={appView.goToLoop} />
8484
<NavItem page="history" onClick={appView.goToHistory} />
85-
<NavItem page="pool" preview onClick={appView.goToPool} />
85+
<NavItem page="pool" badge={l('common.preview')} onClick={appView.goToPool} />
8686
<NavItem page="settings" onClick={appView.goToSettings} />
87-
<NavItem page="connect" onClick={appView.goToConnect} />
87+
<NavItem page="connect" badge={l('common.beta')} onClick={appView.goToConnect} />
8888
</Nav>
8989
</>
9090
);

app/src/i18n/locales/en-US.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"common.months": "month",
1919
"common.months_plural": "months",
2020
"common.preview": "preview",
21+
"common.beta": "beta",
2122
"enums.BalanceMode.receive": "Receiving",
2223
"enums.BalanceMode.send": "Sending",
2324
"enums.BalanceMode.routing": "Routing",
@@ -30,14 +31,17 @@
3031
"cmps.common.PageHeader.exportTip": "Download CSV",
3132
"cmps.common.PageHeader.helpTip": "Take a Tour",
3233
"cmps.common.Wizard.backTip": "Back to Previous",
33-
"cmps.connect.AddSession.create": "Create a New Session",
34+
"cmps.connect.AddSession.create": "Create a new session",
3435
"cmps.connect.AddSession.label": "Label",
3536
"cmps.connect.AddSession.labelHint": "My First Session",
3637
"cmps.connect.AddSession.expiration": "Expiration",
3738
"cmps.connect.AddSession.expirationSuffix": "",
38-
"cmps.connect.ConnectPage.pageTitle": "Terminal Connect",
39-
"cmps.connect.ConnectPage.description": "Through the Terminal Connect page, you can create sessions which allow for you to connect to this Lightning Terminal node remotely from other applications, like Terminal Web. Copy the pairing phrase and paste it into the app in order to make the connection.",
39+
"cmps.connect.ConnectPage.pageTitle": "Lightning Node Connect",
40+
"cmps.connect.ConnectPage.description1": "Lightning Node Connect enables you to connect to this node from the web.",
41+
"cmps.connect.ConnectPage.description2": "Copy the default pairing phrase below to paste it into your app to make your first connection.",
4042
"cmps.connect.ConnectPage.info": "You will need a unique session for each app you wish to connect.",
43+
"cmps.connect.ConnectPage.copyPhraseLabel": "Copy Pairing Phrase",
44+
"cmps.connect.ConnectPage.addlDesc": "If you’ve utilized your default pairing phrase, and want to create additional sessions.",
4145
"cmps.connect.SessionRowHeader.label": "Label",
4246
"cmps.connect.SessionRowHeader.type": "Type",
4347
"cmps.connect.SessionRowHeader.state": "State",
@@ -120,7 +124,7 @@
120124
"cmps.layout.NavMenu.history": "History",
121125
"cmps.layout.NavMenu.pool": "Lightning Pool",
122126
"cmps.layout.NavMenu.settings": "Settings",
123-
"cmps.layout.NavMenu.connect": "Terminal Connect",
127+
"cmps.layout.NavMenu.connect": "Lightning Node Connect",
124128
"cmps.NodeStatus.title": "Node Status",
125129
"cmps.NodeStatus.offchainTip": "Off-chain Funds",
126130
"cmps.NodeStatus.onchainTip": "On-chain Funds",

app/src/store/stores/sessionStore.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ export default class SessionStore {
3939
return descending ? sessions.reverse() : sessions;
4040
}
4141

42+
/** indicates if there are more than one sessions active */
43+
get hasMultiple() {
44+
return this.sortedSessions.length > 1;
45+
}
46+
4247
/**
4348
* queries the LIT api to fetch the list of sessions and stores them
4449
* in the state
@@ -80,7 +85,7 @@ export default class SessionStore {
8085
const countText = count === 0 ? '' : `(${count})`;
8186
await this.addSession(`Default Session ${countText}`, MAX_DATE);
8287
}
83-
} catch (error) {
88+
} catch (error: any) {
8489
this._store.appView.handleError(error, 'Unable to fetch sessions');
8590
}
8691
}
@@ -113,9 +118,10 @@ export default class SessionStore {
113118
await this.fetchSessions();
114119

115120
if (session) {
121+
this.copyPhrase(session.label, session.pairingSecretMnemonic);
116122
return this.sessions.get(hex(session.localPublicKey));
117123
}
118-
} catch (error) {
124+
} catch (error: any) {
119125
this._store.appView.handleError(error, 'Unable to add session');
120126
}
121127
}
@@ -135,18 +141,30 @@ export default class SessionStore {
135141

136142
// fetch all sessions to update the store's state
137143
await this.fetchSessions();
138-
} catch (error) {
144+
} catch (error: any) {
139145
this._store.appView.handleError(error, 'Unable to revoke the session');
140146
}
141147
}
142148

143149
/**
144150
* Copies a pairing phrase to the clipboard
151+
* @param label the session label
145152
* @param phrase the pairing phrase
146153
*/
147-
copyPhrase(phrase: string) {
154+
copyPhrase(label: string, phrase: string) {
148155
copyToClipboard(phrase);
149-
const msg = `Copied Pairing Phrase to clipboard`;
156+
const msg = `Copied Pairing Phrase for '${label}' to clipboard`;
157+
this._store.appView.notify(msg, '', 'success');
158+
}
159+
160+
/**
161+
* Copies a pairing phrase of the first session to the clipboard
162+
*/
163+
copyFirstPhrase() {
164+
if (this.sortedSessions.length === 0) return;
165+
const { pairingSecretMnemonic, label } = this.sortedSessions[0];
166+
copyToClipboard(pairingSecretMnemonic);
167+
const msg = `Copied Pairing Phrase for '${label}' to clipboard`;
150168
this._store.appView.notify(msg, '', 'success');
151169
}
152170
}

0 commit comments

Comments
 (0)