Skip to content

Commit e771594

Browse files
authored
Merge pull request #620 from lightninglabs/disabled-subservers
Front-end integration for disabled subservers
2 parents 6ed39b0 + 5ff3863 commit e771594

File tree

19 files changed

+380
-60
lines changed

19 files changed

+380
-60
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import { renderWithProviders } from 'util/tests';
3+
import { prefixTranslation } from 'util/translate';
4+
import { SubServerStatusMessage } from 'components/common/SubServerRequired';
5+
6+
describe('SubServer Status Message Component', () => {
7+
const render = (isDisabled: boolean, errorMessage?: string) => {
8+
const cmp = (
9+
<SubServerStatusMessage isDisabled={isDisabled} errorMessage={errorMessage} />
10+
);
11+
return renderWithProviders(cmp);
12+
};
13+
14+
it('should display disabled', () => {
15+
const { getByText } = render(true);
16+
const { l } = prefixTranslation('cmps.common.SubServerStatus');
17+
expect(getByText(l('isDisabled'))).toBeInTheDocument();
18+
});
19+
20+
it('should display error', () => {
21+
const { getByText } = render(false);
22+
const { l } = prefixTranslation('cmps.common.SubServerStatus');
23+
expect(getByText(l('isError'))).toBeInTheDocument();
24+
});
25+
26+
it('should match error message', () => {
27+
const { getByText } = render(false, 'Test error message');
28+
expect(getByText('Test error message')).toBeInTheDocument();
29+
});
30+
});

app/src/__tests__/components/loop/LoopPage.spec.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { formatSats } from 'util/formatters';
99
import { renderWithProviders } from 'util/tests';
1010
import { loopListSwaps } from 'util/tests/sampleData';
1111
import { createStore, Store } from 'store';
12+
import { prefixTranslation } from 'util/translate';
1213
import LoopPage from 'components/loop/LoopPage';
1314

1415
const grpcMock = grpc as jest.Mocked<typeof grpc>;
@@ -216,5 +217,15 @@ describe('LoopPage component', () => {
216217
expect(store.settingsStore.channelSort.field).toBeUndefined();
217218
expect(store.settingsStore.channelSort.descending).toBe(true);
218219
});
220+
221+
it('should display subserver disabled message', () => {
222+
const { getByText, store } = render();
223+
const { l } = prefixTranslation('cmps.common.SubServerStatus');
224+
225+
store.subServerStore.subServers.loop.disabled = true;
226+
render();
227+
228+
expect(getByText(l('isDisabled'))).toBeInTheDocument();
229+
});
219230
});
220231
});

app/src/__tests__/components/pool/PoolPage.spec.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import { fireEvent } from '@testing-library/react';
33
import { saveAs } from 'file-saver';
44
import { renderWithProviders } from 'util/tests';
55
import { createStore, Store } from 'store';
6+
import { prefixTranslation } from 'util/translate';
67
import PoolPage from 'components/pool/PoolPage';
78

89
describe('PoolPage', () => {
910
let store: Store;
1011

1112
beforeEach(async () => {
1213
store = createStore();
14+
await store.fetchAllData();
1315
});
1416

1517
const render = () => {
@@ -27,4 +29,14 @@ describe('PoolPage', () => {
2729
fireEvent.click(getByText('download.svg'));
2830
expect(saveAs).toBeCalledWith(expect.any(Blob), 'leases.csv');
2931
});
32+
33+
it('should display subserver disabled message', () => {
34+
const { getByText, store } = render();
35+
const { l } = prefixTranslation('cmps.common.SubServerStatus');
36+
37+
store.subServerStore.subServers.pool.disabled = true;
38+
render();
39+
40+
expect(getByText(l('isDisabled'))).toBeInTheDocument();
41+
});
3042
});

app/src/api/lit.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import * as ACCOUNT from 'types/generated/lit-accounts_pb';
22
import * as SESSION from 'types/generated/lit-sessions_pb';
3+
import * as STATUS from 'types/generated/lit-status_pb';
34
import { Accounts } from 'types/generated/lit-accounts_pb_service';
45
import { Sessions } from 'types/generated/lit-sessions_pb_service';
6+
import { Status } from 'types/generated/lit-status_pb_service';
57
import { b64 } from 'util/strings';
68
import { MAX_DATE } from 'util/constants';
79
import BaseApi from './base';
@@ -84,6 +86,12 @@ class LitApi extends BaseApi<LitEvents> {
8486
const res = await this._grpc.request(Sessions.RevokeSession, req, this._meta);
8587
return res.toObject();
8688
}
89+
90+
async listSubServerStatus(): Promise<STATUS.SubServerStatusResp.AsObject> {
91+
const req = new STATUS.SubServerStatusReq();
92+
const res = await this._grpc.request(Status.SubServerStatus, req, this._meta);
93+
return res.toObject();
94+
}
8795
}
8896

8997
export default LitApi;

app/src/assets/icons/plug.svg

Lines changed: 3 additions & 0 deletions
Loading

app/src/components/base/icons.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ import { ReactComponent as CancelIcon } from 'assets/icons/slash.svg';
3232
import { ReactComponent as UserPlusIcon } from 'assets/icons/user-plus.svg';
3333
import { ReactComponent as QRCodeIcon } from 'assets/icons/qr.svg';
3434
import { ReactComponent as BoltOutlinedIcon } from 'assets/icons/bolt-outlined.svg';
35+
import { ReactComponent as PlugIcon } from 'assets/icons/plug.svg';
3536

3637
interface IconProps {
37-
size?: 'x-small' | 'small' | 'medium' | 'large';
38+
size?: 'x-small' | 'small' | 'medium' | 'large' | 'x-large';
3839
onClick?: () => void;
3940
disabled?: boolean;
4041
}
@@ -51,7 +52,7 @@ const Icon = styled.span<IconProps>`
5152
cursor: pointer;
5253
&:hover {
5354
color: ${props.theme.colors.blue};
54-
background-color: ${props.theme.colors.offWhite};
55+
background-color: ${props.theme.colors.offWhite};
5556
}
5657
`}
5758
@@ -88,6 +89,13 @@ const Icon = styled.span<IconProps>`
8889
width: 36px;
8990
height: 36px;
9091
`}
92+
93+
${props =>
94+
props.size === 'x-large' &&
95+
`
96+
width: 48px;
97+
height: 48px;
98+
`}
9199
`;
92100

93101
export const AlertTriangle = Icon.withComponent(AlertTriangleIcon);
@@ -123,3 +131,4 @@ export const BarChart = Icon.withComponent(BarChartIcon);
123131
export const List = Icon.withComponent(ListIcon);
124132
export const QRCode = Icon.withComponent(QRCodeIcon);
125133
export const BoltOutlined = Icon.withComponent(BoltOutlinedIcon);
134+
export const Plug = Icon.withComponent(PlugIcon);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import React from 'react';
2+
import styled from '@emotion/styled';
3+
import { usePrefixedTranslation } from 'hooks';
4+
import { Plug } from '../base';
5+
import { SubServerStatus } from 'types/state';
6+
7+
const Styled = {
8+
Wrapper: styled.div`
9+
min-height: 100vh;
10+
display: flex;
11+
align-items: center;
12+
justify-content: center;
13+
`,
14+
StatusMessage: styled.div`
15+
display: inline-block;
16+
border-radius: 24px;
17+
padding: 3px 16px 3px 6px;
18+
font-size: ${props => props.theme.sizes.s};
19+
color: ${props => props.theme.colors.lightningGray};
20+
font-weight: 600;
21+
white-space: nowrap;
22+
text-align: center;
23+
24+
svg {
25+
margin-bottom: 16px;
26+
color: ${props => props.theme.colors.gold};
27+
}
28+
`,
29+
Error: styled.div`
30+
font-weight: bold;
31+
`,
32+
};
33+
34+
interface StatusProps {
35+
isDisabled: boolean;
36+
errorMessage?: string;
37+
}
38+
39+
export const SubServerStatusMessage: React.FC<StatusProps> = ({
40+
isDisabled,
41+
errorMessage,
42+
}) => {
43+
const { l } = usePrefixedTranslation('cmps.common.SubServerStatus');
44+
const { Wrapper, StatusMessage, Error } = Styled;
45+
return (
46+
<Wrapper>
47+
<StatusMessage>
48+
<Plug size="x-large" />
49+
50+
{isDisabled ? (
51+
<p>{l('isDisabled')}</p>
52+
) : (
53+
<>
54+
<p>{l('isError')}</p>
55+
<Error>{errorMessage}</Error>
56+
</>
57+
)}
58+
</StatusMessage>
59+
</Wrapper>
60+
);
61+
};
62+
63+
interface Props {
64+
status: SubServerStatus;
65+
}
66+
67+
const SubServerRequired: React.FC<Props> = ({ status, children }) => {
68+
if (status.disabled) {
69+
return <SubServerStatusMessage isDisabled />;
70+
} else if (status.error) {
71+
return <SubServerStatusMessage isDisabled={false} errorMessage={status.error} />;
72+
}
73+
74+
return <>{children}</>;
75+
};
76+
77+
export default SubServerRequired;

app/src/components/history/HistoryPage.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import styled from '@emotion/styled';
44
import { usePrefixedTranslation } from 'hooks';
55
import { useStore } from 'store';
66
import PageHeader from 'components/common/PageHeader';
7+
import SubServerRequired from 'components/common/SubServerRequired';
78
import HistoryList from './HistoryList';
89

910
const Styled = {
@@ -14,14 +15,16 @@ const Styled = {
1415

1516
const HistoryPage: React.FC = () => {
1617
const { l } = usePrefixedTranslation('cmps.history.HistoryPage');
17-
const { swapStore } = useStore();
18+
const { swapStore, subServerStore } = useStore();
1819

1920
const { Wrapper } = Styled;
2021
return (
21-
<Wrapper>
22-
<PageHeader title={l('pageTitle')} onExportClick={swapStore.exportSwaps} />
23-
<HistoryList />
24-
</Wrapper>
22+
<SubServerRequired status={subServerStore.subServers.loop}>
23+
<Wrapper>
24+
<PageHeader title={l('pageTitle')} onExportClick={swapStore.exportSwaps} />
25+
<HistoryList />
26+
</Wrapper>
27+
</SubServerRequired>
2528
);
2629
};
2730

app/src/components/loop/LoopPage.tsx

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { usePrefixedTranslation } from 'hooks';
55
import { useStore } from 'store';
66
import { Badge } from 'components/base';
77
import PageHeader from 'components/common/PageHeader';
8+
import SubServerRequired from 'components/common/SubServerRequired';
89
import ChannelList from './ChannelList';
910
import LoopActions from './LoopActions';
1011
import LoopTiles from './LoopTiles';
@@ -26,6 +27,7 @@ const LoopPage: React.FC = () => {
2627
registerSidecarView,
2728
channelStore,
2829
nodeStore,
30+
subServerStore,
2931
} = useStore();
3032

3133
const title = (
@@ -41,26 +43,28 @@ const LoopPage: React.FC = () => {
4143

4244
const { PageWrap } = Styled;
4345
return (
44-
<PageWrap>
45-
{appView.processingSwapsVisible ? (
46-
<ProcessingSwaps />
47-
) : buildSwapView.showWizard ? (
48-
<SwapWizard />
49-
) : registerSidecarView.showWizard ? (
50-
<SidecarWizard />
51-
) : (
52-
<>
53-
<PageHeader
54-
title={title}
55-
onHelpClick={appView.showTour}
56-
onExportClick={channelStore.exportChannels}
57-
/>
58-
<LoopTiles />
59-
<LoopActions />
60-
</>
61-
)}
62-
<ChannelList />
63-
</PageWrap>
46+
<SubServerRequired status={subServerStore.subServers.loop}>
47+
<PageWrap>
48+
{appView.processingSwapsVisible ? (
49+
<ProcessingSwaps />
50+
) : buildSwapView.showWizard ? (
51+
<SwapWizard />
52+
) : registerSidecarView.showWizard ? (
53+
<SidecarWizard />
54+
) : (
55+
<>
56+
<PageHeader
57+
title={title}
58+
onHelpClick={appView.showTour}
59+
onExportClick={channelStore.exportChannels}
60+
/>
61+
<LoopTiles />
62+
<LoopActions />
63+
</>
64+
)}
65+
<ChannelList />
66+
</PageWrap>
67+
</SubServerRequired>
6468
);
6569
};
6670

0 commit comments

Comments
 (0)