Skip to content

Commit 6436571

Browse files
committed
tour: cosmetic improvements to the tour
1 parent 2f6c9fd commit 6436571

File tree

9 files changed

+181
-33
lines changed

9 files changed

+181
-33
lines changed

app/src/__tests__/components/tour/TourHost.spec.tsx

+10-6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ describe('TourHost component', () => {
4646
expect(getByText('Welcome to Lightning Terminal!')).toBeInTheDocument();
4747

4848
fireEvent.click(getByText("Yes! Let's Go"));
49+
fireEvent.click(getByText('Next'));
4950
expect(store.settingsStore.sidebarVisible).toBe(true);
5051

5152
fireEvent.click(getByText('Next'));
@@ -60,13 +61,16 @@ describe('TourHost component', () => {
6061
expect(getByText('Welcome to Lightning Terminal!')).toBeInTheDocument();
6162

6263
fireEvent.click(getByText("Yes! Let's Go"));
64+
expect(getByText('New to Loop?')).toBeInTheDocument();
65+
66+
fireEvent.click(getByText('Next'));
6367
expect(getByText(l('nodeStatus'))).toBeInTheDocument();
6468

6569
// sample data is fetch after step #1 and we need to wait for it
6670
await waitFor(() => expect(store.swapStore.sortedSwaps).toHaveLength(7));
6771

6872
fireEvent.click(getByText('Next'));
69-
expect(getByText(l('history'))).toBeInTheDocument();
73+
expect(getByText(firstLine(l('history')))).toBeInTheDocument();
7074

7175
fireEvent.click(getByText('Next'));
7276
expect(getByText(l('inbound'))).toBeInTheDocument();
@@ -75,7 +79,7 @@ describe('TourHost component', () => {
7579
expect(getByText(l('outbound'))).toBeInTheDocument();
7680

7781
fireEvent.click(getByText('Next'));
78-
expect(getByText(l('channelList'))).toBeInTheDocument();
82+
expect(getByText('channel needs your immediate attention')).toBeInTheDocument();
7983

8084
fireEvent.click(getByText('Next'));
8185
expect(getByText(l('channelListReceive'))).toBeInTheDocument();
@@ -84,13 +88,13 @@ describe('TourHost component', () => {
8488
expect(getByText(l('channelListSend'))).toBeInTheDocument();
8589

8690
fireEvent.click(getByText('Next'));
87-
expect(getByText(l('channelListFee'))).toBeInTheDocument();
91+
expect(getByText(firstLine(l('channelListFee')))).toBeInTheDocument();
8892

8993
fireEvent.click(getByText('Next'));
9094
expect(getByText(l('channelListUptime'))).toBeInTheDocument();
9195

9296
fireEvent.click(getByText('Next'));
93-
expect(getByText(l('channelListPeer'))).toBeInTheDocument();
97+
expect(getByText(firstLine(l('channelListPeer')))).toBeInTheDocument();
9498

9599
fireEvent.click(getByText('Next'));
96100
expect(getByText(l('channelListCapacity'))).toBeInTheDocument();
@@ -99,10 +103,10 @@ describe('TourHost component', () => {
99103
expect(getByText(l('export'))).toBeInTheDocument();
100104

101105
fireEvent.click(getByText('Next'));
102-
expect(getByText('Lightning Loop')).toBeInTheDocument();
106+
expect(getByText("Let's perform a Loop!")).toBeInTheDocument();
103107

104108
fireEvent.click(getByText('Loop', { selector: 'button' }));
105-
expect(getByText(l('loopActions'))).toBeInTheDocument();
109+
expect(getByText(firstLine(l('loopActions')))).toBeInTheDocument();
106110

107111
fireEvent.click(getByText('Next'));
108112
expect(getByText(firstLine(l('channelListSelect')))).toBeInTheDocument();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
import { ReactourStepContentArgs } from 'reactour';
3+
import { observer } from 'mobx-react-lite';
4+
import { usePrefixedTranslation } from 'hooks';
5+
import StatusDot from 'components/common/StatusDot';
6+
import TextStep from './TextStep';
7+
8+
const ChannelListStep: React.FC<ReactourStepContentArgs> = props => {
9+
const { l } = usePrefixedTranslation('cmps.tour.ChannelListStep');
10+
11+
return (
12+
<TextStep {...props}>
13+
<p>{l('desc')}</p>
14+
<p>{l('traffic')}</p>
15+
<table>
16+
<tbody>
17+
{['error', 'warn', 'success'].map(s => (
18+
<tr key={s}>
19+
<td>
20+
<StatusDot status={s as any} />
21+
</td>
22+
<td>{l(s)}</td>
23+
</tr>
24+
))}
25+
</tbody>
26+
</table>
27+
</TextStep>
28+
);
29+
};
30+
31+
export default observer(ChannelListStep);
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
import { ReactourStepContentArgs } from 'reactour';
3+
import { observer } from 'mobx-react-lite';
4+
import { usePrefixedTranslation } from 'hooks';
5+
import TextStep from './TextStep';
6+
7+
const LoopInfoStep: React.FC<ReactourStepContentArgs> = props => {
8+
const { l } = usePrefixedTranslation('cmps.tour.LoopInfoStep');
9+
10+
return (
11+
<TextStep {...props}>
12+
<p>
13+
<strong>{l('new')}</strong>
14+
</p>
15+
<p>{l('desc')}</p>
16+
<p>
17+
{l('learn1')}{' '}
18+
<a
19+
href="https://lightning.engineering/loop"
20+
target="_blank"
21+
rel="noopener noreferrer"
22+
>
23+
{l('learn2')}
24+
</a>{' '}
25+
{l('learn3')}
26+
</p>
27+
</TextStep>
28+
);
29+
};
30+
31+
export default observer(LoopInfoStep);
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
import { ReactourStepContentArgs } from 'reactour';
3+
import { observer } from 'mobx-react-lite';
4+
import { usePrefixedTranslation } from 'hooks';
5+
import { Bitcoin, Bolt } from 'components/base';
6+
import { styled } from 'components/theme';
7+
import TextStep from './TextStep';
8+
9+
const Styled = {
10+
Legend: styled.p`
11+
> span {
12+
display: flex;
13+
margin-top: 10px;
14+
}
15+
`,
16+
BoltIcon: styled(Bolt)`
17+
background-color: ${props => props.theme.colors.darkBlue};
18+
color: ${props => props.theme.colors.white};
19+
border-radius: 32px;
20+
margin-right: 10px;
21+
`,
22+
BitcoinIcon: styled(Bitcoin)`
23+
background-color: ${props => props.theme.colors.darkBlue};
24+
color: ${props => props.theme.colors.white};
25+
border-radius: 32px;
26+
margin-right: 10px;
27+
`,
28+
};
29+
30+
const NodeStatusStep: React.FC<ReactourStepContentArgs> = props => {
31+
const { l } = usePrefixedTranslation('cmps.tour.NodeStatusStep');
32+
33+
const { Legend, BoltIcon, BitcoinIcon } = Styled;
34+
return (
35+
<TextStep {...props}>
36+
<p>{l('desc')}</p>
37+
<Legend>
38+
<span>
39+
<BoltIcon title="bolt" size="small" />
40+
{l('ln')}
41+
</span>
42+
<span>
43+
<BitcoinIcon title="on-chain" size="small" />
44+
{l('onchain')}
45+
</span>
46+
</Legend>
47+
</TextStep>
48+
);
49+
};
50+
51+
export default observer(NodeStatusStep);

app/src/components/tour/TourHost.tsx

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
1+
/* eslint-disable react/display-name */
12
import React from 'react';
23
import Tour, { ReactourStep } from 'reactour';
34
import { observer } from 'mobx-react-lite';
45
import { useTheme } from 'emotion-theming';
56
import { useStore } from 'store';
67
import { styled, Theme } from 'components/theme';
8+
import ChannelListStep from './ChannelListStep';
9+
import LoopInfoStep from './LoopInfoStep';
10+
import NodeStatusStep from './NodeStatusStep';
711
import SuccessStep from './SuccessStep';
812
import { createTextStep } from './TextStep';
913
import WelcomeStep from './WelcomeStep';
1014

1115
const tourSteps: ReactourStep[] = [
1216
{
13-
// eslint-disable-next-line react/display-name
1417
content: p => <WelcomeStep {...p} />,
1518
style: { maxWidth: 900 },
1619
},
20+
{
21+
content: p => <LoopInfoStep {...p} />,
22+
style: { maxWidth: 900 },
23+
},
1724
{
1825
selector: '[data-tour="node-status"]',
19-
content: createTextStep('nodeStatus'),
26+
content: p => <NodeStatusStep {...p} />,
2027
},
2128
{
2229
selector: '[data-tour="history"]',
@@ -33,7 +40,8 @@ const tourSteps: ReactourStep[] = [
3340
},
3441
{
3542
selector: '[data-tour="channel-list"]',
36-
content: createTextStep('channelList'),
43+
content: p => <ChannelListStep {...p} />,
44+
style: { maxWidth: 600 },
3745
},
3846
{
3947
selector: '[data-tour="channel-list-receive"]',
@@ -71,6 +79,7 @@ const tourSteps: ReactourStep[] = [
7179
{
7280
selector: '[data-tour="loop-actions"]',
7381
content: createTextStep('loopActions'),
82+
style: { maxWidth: 500 },
7483
stepInteraction: false,
7584
},
7685
{
@@ -106,14 +115,17 @@ const tourSteps: ReactourStep[] = [
106115
content: createTextStep('swapProgress'),
107116
},
108117
{
109-
// eslint-disable-next-line react/display-name
110118
content: p => <SuccessStep {...p} />,
111119
style: { maxWidth: 900 },
112120
},
113121
];
114122

115123
const Styled = {
116124
Tour: styled(Tour)`
125+
&.reactour__helper {
126+
border-radius: 10px;
127+
}
128+
117129
[data-tour-elem='badge'] {
118130
font-family: ${props => props.theme.fonts.open.regular};
119131
font-size: ${props => props.theme.sizes.xxs};

app/src/components/tour/WelcomeStep.tsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ const Styled = {
1111
font-size: ${props => props.theme.sizes.xs};
1212
font-style: italic;
1313
opacity: 0.8;
14-
margin-bottom: 50px;
14+
margin-bottom: 40px;
1515
`,
1616
Footer: styled.div`
17-
display: flex;
18-
justify-content: space-between;
17+
margin-top: 30px;
1918
`,
2019
LinkButton: styled(Button)`
21-
color: ${props => props.theme.colors.darkBlue};
20+
color: ${props => props.theme.colors.gray};
2221
padding: 0;
2322
min-width: auto;
2423
height: auto;
24+
margin-left: 40px;
2525
2626
&:hover {
2727
color: ${props => props.theme.colors.blue};
@@ -47,6 +47,7 @@ const WelcomeStep: React.FC<ReactourStepContentArgs> = props => {
4747
return (
4848
<TextStep header={l('header')} showNext={false} {...props}>
4949
<p>{l('desc')}</p>
50+
<p>{l('tour')}</p>
5051
<p>
5152
{l('walkthrough1')}{' '}
5253
<a

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

+22-9
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,18 @@
8383
"cmps.tour.TextStep.next": "Next",
8484
"cmps.tour.TextStep.nodeStatus": "Here are the confirmed balances on the Lightning Network and on-chain",
8585
"cmps.tour.TextStep.export": "Click here if you want to export your channels as a CSV file",
86-
"cmps.tour.TextStep.history": "This tile displays the two most recent loops that have been initiated. It is perfectly safe to perform multiple loops at one time.",
86+
"cmps.tour.TextStep.history": "This tile displays the two most recent loops that have been initiated. It is perfectly safe to perform multiple loops at one time.\n Clicking on the Expand icon will show you more details about the currently processing Loops.",
8787
"cmps.tour.TextStep.inbound": "This tile displays the amount of Bitcoin that this node can receive over your Lightning channels",
8888
"cmps.tour.TextStep.outbound": "This tile displays the amount of Bitcoin that this node can send over your Lightning channels",
89-
"cmps.tour.TextStep.channelList": "This is the list of open channels that this node has with other peers on the Lightning Network",
90-
"cmps.tour.TextStep.channelListReceive": "The amount that can be received over the channel",
91-
"cmps.tour.TextStep.channelListSend": "The amount that can be sent over this channel",
92-
"cmps.tour.TextStep.channelListFee": "The routing fee rate charged by the peer to receive payments over the channel. The percent is rounded to two decimal places. Hover over the value to see the exact rate expressed as parts-per-million.",
89+
"cmps.tour.TextStep.channelListReceive": "The amount that can be received over each channel",
90+
"cmps.tour.TextStep.channelListSend": "The amount that can be sent over each channel",
91+
"cmps.tour.TextStep.channelListFee": "The routing fee rate charged by the peer to receive payments over the channel.\n The percent is rounded to two decimal places. Hover over the value to see the exact rate expressed as parts-per-million.",
9392
"cmps.tour.TextStep.channelListUptime": "The uptime percentage of the channel peer",
94-
"cmps.tour.TextStep.channelListPeer": "The alias of the channel peer. Hover over this field to see the peer's pubkey.",
93+
"cmps.tour.TextStep.channelListPeer": "The alias of the channel peer.\n Hover over this field to see the peer's pubkey.",
9594
"cmps.tour.TextStep.channelListCapacity": "The total capacity of the channel",
96-
"cmps.tour.TextStep.loop": "<strong>Lightning Loop</strong> can be used to transfer your Bitcoin balance between the Lightning Network and the on-chain Bitcoin network. This allows you to easily alter the balances of your channels without needing to close and reopen them, which extends the lifetime of your channels, and also enables operations to be batched as well.\n Let's perform a Loop!\n Click on the <strong>Loop</strong> button to start.",
95+
"cmps.tour.TextStep.loop": "Let's perform a Loop!\n Click on the <strong>Loop</strong> button to start.",
9796
"cmps.tour.TextStep.channelListSelect": "Optionally, you can select one or more channels if you want the swap to only transfer funds over those chosen channels. This is helpful if you want to only adjust the balance of specific channels.\n If you choose more than one, there is no guarantee how much funds will be transferred over each channel. LND will use its knowledge of the network to determine how to best split up the payment, if necessary. It is possible that only one of the channels will be used if the payment can be successfully routed through it.\n If you do not care which channels to use, then do not select any. The Lightning payment will be routed through any channel(s) with enough balance to allow it to succeed.",
98-
"cmps.tour.TextStep.loopActions": "The action bar displays information about the selected channels and the buttons to Loop Out or Loop In.",
97+
"cmps.tour.TextStep.loopActions": "The action bar displays information about the selected channels and the buttons to Loop Out or Loop In.\n Use <strong>Loop Out</strong> when you want to transfer funds from your Lightning channels to your on-chain wallet.\n Use <strong>Loop In</strong> when you want to transfer funds from your on-chain wallet into your Lightning channels.",
9998
"cmps.tour.TextStep.loopOut": "We will use Loop Out to transfer funds from your channel balances to your on-chain wallet.\n Click on the <strong>Loop Out</strong> button to continue.",
10099
"cmps.tour.TextStep.loopAmount": "You now need to specify the amount you would like to Loop Out. Drag the slider to adjust the amount.\n Click the <strong>Next</strong> button to continue.",
101100
"cmps.tour.TextStep.loopReview": "Review the Loop amount and the fee. Keep in mind that you will only be charged the fee. The Loop amount will remain on your node once the swap completes. The balance will just be shifted from your channel balance to your on-chain wallet balance.\n Click the <strong>Confirm</strong> button to continue.",
@@ -104,14 +103,28 @@
104103
"cmps.tour.TextStep.swapProgress": "Once the progress bar turns green, your swap has been completed successfully.",
105104
"cmps.tour.TextStep.congrats": "Congratulations!\n You have completed the tour.\n Happy Looping!",
106105
"cmps.tour.WelcomeStep.header": "Welcome to Lightning Terminal!",
107-
"cmps.tour.WelcomeStep.desc": "This tour will walk you through the different sections of the dashboard and show you how to perform a Loop.",
106+
"cmps.tour.WelcomeStep.desc": "A browser-based interface for Loop, presenting a visual representation of your of your channels and balances. Easily identify inefficient channels, perform loops with just a few clicks, and monitor the progress of your ongoing Loops.",
107+
"cmps.tour.WelcomeStep.tour": "This tour will walk you through the different sections of the dashboard and show you how to perform a Loop.",
108108
"cmps.tour.WelcomeStep.walkthrough1": "You may also reference the ",
109109
"cmps.tour.WelcomeStep.walkthrough2": "Walkthrough",
110110
"cmps.tour.WelcomeStep.walkthrough3": " document for additional guidance on how to use the product.",
111111
"cmps.tour.WelcomeStep.note": "Note: sample data will be displayed during this tour, so none of your actual funds will be touched.",
112112
"cmps.tour.WelcomeStep.question": "Would you like to take a tour?",
113113
"cmps.tour.WelcomeStep.noThanks": "No Thanks",
114114
"cmps.tour.WelcomeStep.yes": "Yes! Let's Go",
115+
"cmps.tour.LoopInfoStep.new": "New to Loop?",
116+
"cmps.tour.LoopInfoStep.desc": "Lightning Loop can be used to transfer your Bitcoin balance between the Lightning Network and the on-chain Bitcoin network. This allows you to easily alter the balances of your channels without needing to close and reopen them, which extends the lifetime of your channels, and also enables operations to be batched as well.",
117+
"cmps.tour.LoopInfoStep.learn1": "To learn more, visit the",
118+
"cmps.tour.LoopInfoStep.learn2": "Loop",
119+
"cmps.tour.LoopInfoStep.learn3": "website. Otherwise, continue with the tour to try it out now.",
120+
"cmps.tour.NodeStatusStep.desc": "Here are the confirmed balances on the Lightning Network and on-chain",
121+
"cmps.tour.NodeStatusStep.ln": "Lightning Network",
122+
"cmps.tour.NodeStatusStep.onchain": "On-chain",
123+
"cmps.tour.ChannelListStep.desc": "This is the list of open channels that this node has with other peers on the Lightning Network.",
124+
"cmps.tour.ChannelListStep.traffic": "It uses a traffic light system to help you prioritize the channels that need your attention.",
125+
"cmps.tour.ChannelListStep.error": "channel needs your immediate attention",
126+
"cmps.tour.ChannelListStep.warn": "you should attend to the channel soon",
127+
"cmps.tour.ChannelListStep.success": "all is well, and no extra attention is required",
115128
"cmps.tour.SuccessStep.header": "Congratulations!",
116129
"cmps.tour.SuccessStep.complete": "You have completed the tour. Happy Looping!",
117130
"cmps.tour.SuccessStep.close": "Close",

app/src/store/stores/buildSwapStore.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,12 @@ class BuildSwapStore {
408408
*/
409409
@action.bound
410410
requestSwap() {
411-
const delay = process.env.NODE_ENV !== 'test' ? SWAP_ABORT_DELAY : 1;
411+
const delay =
412+
process.env.NODE_ENV === 'test'
413+
? 1 // use a 1 ms delay for unit tests
414+
: this._store.uiStore.tourVisible
415+
? 1500 // use a 1.5 second delay during the tour
416+
: SWAP_ABORT_DELAY;
412417
const { amount, direction, quote } = this;
413418
this._store.log.info(
414419
`executing ${direction} for ${amount} sats (delaying for ${delay}ms)`,

app/src/store/stores/uiStore.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export default class UiStore {
106106
setTourActiveStep(step: number) {
107107
this.tourActiveStep = step;
108108

109-
if (step === 1) {
109+
if (step === 2) {
110110
// #1 is the node-status step
111111
// show the sidebar if autoCollapse is enabled
112112
if (!this._store.settingsStore.sidebarVisible) {
@@ -121,26 +121,26 @@ export default class UiStore {
121121
this._store.unsubscribeFromStreams();
122122
// fetch all the sample data from the backend
123123
this._store.fetchAllData();
124-
} else if (step === 2) {
125-
// #2 is the export icon
124+
} else if (step === 3) {
125+
// #3 is the export icon
126126
// hide the sidebar if autoCollapse is enabled
127127
if (this._store.settingsStore.autoCollapse) {
128128
this._store.settingsStore.sidebarVisible = false;
129129
}
130-
} else if (step === 3) {
131-
// #3 is the history step
130+
} else if (step === 4) {
131+
// #4 is the history step
132132
// change the most recent swap to be pending
133133
this._store.swapStore.sortedSwaps[0].state = SwapState.INITIATED;
134134
// set the timestamp far in the future so it doesn't automatically disappear
135135
// from the Processing Loops list after 5 mins
136136
const tomorrow = Date.now() + 24 * 60 * 60 * 1000;
137137
this._store.swapStore.sortedSwaps[0].lastUpdateTime = tomorrow * 1000 * 1000;
138-
} else if (step === 21 /* swap-progress */) {
139-
// #21 is the swap-progress step
138+
} else if (step === 22 /* swap-progress */) {
139+
// #22 is the swap-progress step
140140
// force the swap to be 100% complete
141141
this._store.swapStore.sortedSwaps[0].state = SwapState.SUCCESS;
142-
} else if (step === 22 /* congrats */) {
143-
// #22 is the congrats step
142+
} else if (step === 23 /* congrats */) {
143+
// #23 is the congrats step
144144
// hide the processing swaps section
145145
this.processingSwapsVisible = false;
146146
}

0 commit comments

Comments
 (0)