Skip to content

Commit 3b58e8e

Browse files
authored
Merge pull request #451 from lightninglabs/custom-connect-ui
Custom user permissions UI
2 parents fa90690 + 3bc24b5 commit 3b58e8e

26 files changed

+1332
-47
lines changed

app/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@emotion/react": "11.4.0",
2222
"@emotion/styled": "11.3.0",
2323
"@improbable-eng/grpc-web": "0.14.0",
24+
"@types/react-collapse": "^5.0.1",
2425
"big.js": "6.1.1",
2526
"bootstrap": "4.6.1",
2627
"buffer": "6.0.3",
@@ -40,8 +41,10 @@
4041
"qrcode.react": "^3.1.0",
4142
"rc-dialog": "^8.9.0",
4243
"rc-select": "11.5.0",
44+
"rc-switch": "^4.0.0",
4345
"rc-tooltip": "4.2.1",
4446
"react": "17.0.2",
47+
"react-collapse": "^5.1.1",
4548
"react-dom": "17.0.2",
4649
"react-i18next": "11.7.0",
4750
"react-router-dom": "^6.3.0",

app/src/App.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
@import '../node_modules/rc-tooltip/assets/bootstrap_white.css';
1919
@import '../node_modules/rc-dialog/assets/index.css';
2020
@import './assets/styles/rc-select.scss';
21+
@import './assets/styles/rc-switch.scss';
2122

2223
// react-toastify styles
2324
@import '../node_modules/react-toastify/dist/ReactToastify.css';

app/src/AppRoutes.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ const LazyHistoryPage = React.lazy(() => import('components/history/HistoryPage'
99
const LazyPoolPage = React.lazy(() => import('components/pool/PoolPage'));
1010
const LazySettingsPage = React.lazy(() => import('components/settings/SettingsPage'));
1111
const LazyConnectPage = React.lazy(() => import('components/connect/ConnectPage'));
12+
const LazyCustomSessionPage = React.lazy(
13+
() => import('components/connect/CustomSessionPage'),
14+
);
1215

1316
const AppRoutes: React.FC = () => {
1417
return (
@@ -63,6 +66,14 @@ const AppRoutes: React.FC = () => {
6366
</Layout>
6467
}
6568
/>
69+
<Route
70+
path="connect/custom"
71+
element={
72+
<Layout>
73+
<LazyCustomSessionPage />
74+
</Layout>
75+
}
76+
/>
6677
</Route>
6778
</Routes>
6879
);

app/src/api/lit.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import * as LIT from 'types/generated/lit-sessions_pb';
1+
import * as ACCOUNT from 'types/generated/lit-accounts_pb';
2+
import * as SESSION from 'types/generated/lit-sessions_pb';
3+
import { Accounts } from 'types/generated/lit-accounts_pb_service';
24
import { Sessions } from 'types/generated/lit-sessions_pb_service';
35
import { b64 } from 'util/strings';
6+
import { MAX_DATE } from 'util/constants';
47
import BaseApi from './base';
58
import GrpcClient from './grpc';
69

@@ -16,24 +19,46 @@ class LitApi extends BaseApi<LitEvents> {
1619
this._grpc = grpc;
1720
}
1821

22+
/**
23+
* call the Lit `CreateAccount` RPC and return the response
24+
*/
25+
async createAccount(
26+
accountBalance: number,
27+
expirationDate: Date,
28+
): Promise<ACCOUNT.CreateAccountResponse.AsObject> {
29+
const req = new ACCOUNT.CreateAccountRequest();
30+
req.setAccountBalance(accountBalance.toString());
31+
32+
if (expirationDate === MAX_DATE) {
33+
req.setExpirationDate('0');
34+
} else {
35+
req.setExpirationDate(Math.floor(expirationDate.getTime() / 1000).toString());
36+
}
37+
38+
const res = await this._grpc.request(Accounts.CreateAccount, req, this._meta);
39+
return res.toObject();
40+
}
41+
1942
/**
2043
* call the Lit `AddSession` RPC and return the response
2144
*/
2245
async addSession(
2346
label: string,
24-
sessionType: LIT.SessionTypeMap[keyof LIT.SessionTypeMap],
47+
sessionType: SESSION.SessionTypeMap[keyof SESSION.SessionTypeMap],
2548
expiry: Date,
2649
mailboxServerAddr: string,
2750
devServer: boolean,
28-
macaroonCustomPermissions: Array<LIT.MacaroonPermission>,
29-
): Promise<LIT.AddSessionResponse.AsObject> {
30-
const req = new LIT.AddSessionRequest();
51+
macaroonCustomPermissions: Array<SESSION.MacaroonPermission>,
52+
accountId: string,
53+
): Promise<SESSION.AddSessionResponse.AsObject> {
54+
const req = new SESSION.AddSessionRequest();
3155
req.setLabel(label);
3256
req.setSessionType(sessionType);
3357
req.setExpiryTimestampSeconds(Math.floor(expiry.getTime() / 1000).toString());
3458
req.setMailboxServerAddr(mailboxServerAddr);
3559
req.setDevServer(devServer);
3660
req.setMacaroonCustomPermissionsList(macaroonCustomPermissions);
61+
req.setAccountId(accountId);
3762

3863
const res = await this._grpc.request(Sessions.AddSession, req, this._meta);
3964
return res.toObject();
@@ -42,8 +67,8 @@ class LitApi extends BaseApi<LitEvents> {
4267
/**
4368
* call the Lit `ListSessions` RPC and return the response
4469
*/
45-
async listSessions(): Promise<LIT.ListSessionsResponse.AsObject> {
46-
const req = new LIT.ListSessionsRequest();
70+
async listSessions(): Promise<SESSION.ListSessionsResponse.AsObject> {
71+
const req = new SESSION.ListSessionsRequest();
4772
const res = await this._grpc.request(Sessions.ListSessions, req, this._meta);
4873
return res.toObject();
4974
}
@@ -53,8 +78,8 @@ class LitApi extends BaseApi<LitEvents> {
5378
*/
5479
async revokeSession(
5580
localPublicKey: string,
56-
): Promise<LIT.RevokeSessionResponse.AsObject> {
57-
const req = new LIT.RevokeSessionRequest();
81+
): Promise<SESSION.RevokeSessionResponse.AsObject> {
82+
const req = new SESSION.RevokeSessionRequest();
5883
req.setLocalPublicKey(b64(localPublicKey));
5984
const res = await this._grpc.request(Sessions.RevokeSession, req, this._meta);
6085
return res.toObject();

app/src/assets/styles/rc-switch.scss

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
$switchPrefixCls: rc-switch;
2+
3+
$duration: 0.3s;
4+
5+
.rc-switch {
6+
position: relative;
7+
display: inline-block;
8+
box-sizing: border-box;
9+
width: 44px;
10+
height: 22px;
11+
line-height: 20px;
12+
padding: 0;
13+
vertical-align: middle;
14+
border-radius: 20px 20px;
15+
border: 1px solid #ccc;
16+
background-color: #ccc;
17+
cursor: pointer;
18+
transition: all $duration cubic-bezier(0.35, 0, 0.25, 1);
19+
20+
&-inner {
21+
color: #fff;
22+
font-size: 12px;
23+
position: absolute;
24+
left: 24px;
25+
top: 0;
26+
}
27+
28+
&:after {
29+
position: absolute;
30+
width: 18px;
31+
height: 18px;
32+
left: 2px;
33+
top: 1px;
34+
border-radius: 50% 50%;
35+
background-color: #fff;
36+
content: ' ';
37+
cursor: pointer;
38+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26);
39+
transform: scale(1);
40+
transition: left $duration cubic-bezier(0.35, 0, 0.25, 1);
41+
animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1);
42+
animation-duration: $duration;
43+
animation-name: rcSwitchOff;
44+
}
45+
46+
&:hover:after {
47+
transform: scale(1.1);
48+
animation-name: rcSwitchOn;
49+
}
50+
51+
&:focus {
52+
box-shadow: 0 0 0 2px tint(#2db7f5, 80%);
53+
outline: none;
54+
}
55+
56+
&-checked {
57+
border: 1px solid #87d068;
58+
background-color: #87d068;
59+
60+
.rc-switch-inner {
61+
left: 6px;
62+
}
63+
64+
&:after {
65+
left: 22px;
66+
}
67+
}
68+
69+
&-disabled {
70+
cursor: no-drop;
71+
background: #ccc;
72+
border-color: #ccc;
73+
74+
&:after {
75+
background: #9e9e9e;
76+
animation-name: none;
77+
cursor: no-drop;
78+
}
79+
80+
&:hover:after {
81+
transform: scale(1);
82+
animation-name: none;
83+
}
84+
}
85+
86+
&-label {
87+
display: inline-block;
88+
line-height: 20px;
89+
font-size: 14px;
90+
padding-left: 10px;
91+
vertical-align: middle;
92+
white-space: normal;
93+
pointer-events: none;
94+
user-select: text;
95+
}
96+
}
97+
98+
@keyframes rcSwitchOn {
99+
0% {
100+
transform: scale(1);
101+
}
102+
50% {
103+
transform: scale(1.25);
104+
}
105+
100% {
106+
transform: scale(1.1);
107+
}
108+
}
109+
110+
@keyframes rcSwitchOff {
111+
0% {
112+
transform: scale(1.1);
113+
}
114+
100% {
115+
transform: scale(1);
116+
}
117+
}

app/src/components/base/grid.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
import React, { CSSProperties } from 'react';
22

3+
/**
4+
* This component represents a container in the bootstrap Grid layout
5+
*/
6+
export const Container: React.FC<{
7+
className?: string;
8+
style?: CSSProperties;
9+
}> = ({ children, className, style }) => {
10+
const cn: string[] = ['container'];
11+
className && cn.push(className);
12+
return (
13+
<div className={cn.join(' ')} style={style}>
14+
{children}
15+
</div>
16+
);
17+
};
18+
319
/**
420
* This component represents a Row in the bootstrap Grid layout
521
*/
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React, { ReactNode } from 'react';
2+
import styled from '@emotion/styled';
3+
4+
const Styled = {
5+
Wrapper: styled.div`
6+
position: relative;
7+
font-family: ${props => props.theme.fonts.work.light};
8+
font-weight: 300;
9+
font-size: ${props => props.theme.sizes.s};
10+
color: ${props => props.theme.colors.offWhite};
11+
`,
12+
Input: styled.input`
13+
color: ${props => props.theme.colors.offWhite};
14+
background-color: ${props => props.theme.colors.overlay};
15+
border-width: 0;
16+
border-bottom: 1px solid ${props => props.theme.colors.gray};
17+
padding: 5px 40px 5px 5px;
18+
width: 100%;
19+
20+
&:active,
21+
&:focus {
22+
outline: none;
23+
border-bottom-color: ${props => props.theme.colors.white};
24+
}
25+
26+
&::placeholder {
27+
color: ${props => props.theme.colors.gray};
28+
}
29+
30+
// Fix color of the date picker icon in chrome
31+
::-webkit-calendar-picker-indicator {
32+
filter: invert(1);
33+
}
34+
`,
35+
Extra: styled.div`
36+
position: absolute;
37+
top: 0;
38+
right: 0;
39+
background-color: transparent;
40+
padding: 5px;
41+
`,
42+
};
43+
44+
interface Props {
45+
label?: string;
46+
value?: string;
47+
extra?: ReactNode;
48+
placeholder?: string;
49+
className?: string;
50+
onChange?: (value: string) => void;
51+
}
52+
53+
const FormDate: React.FC<Props> = ({
54+
label,
55+
value,
56+
placeholder,
57+
extra,
58+
className,
59+
onChange,
60+
}) => {
61+
const { Wrapper, Input, Extra } = Styled;
62+
return (
63+
<Wrapper className={className}>
64+
<Input
65+
type="date"
66+
value={value}
67+
onChange={e => onChange && onChange(e.target.value)}
68+
placeholder={placeholder}
69+
aria-label={label}
70+
/>
71+
{extra && <Extra>{extra}</Extra>}
72+
</Wrapper>
73+
);
74+
};
75+
76+
export default FormDate;

app/src/components/common/FormField.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ interface Props {
2222
info?: ReactNode;
2323
error?: ReactNode;
2424
tip?: string;
25+
className?: string;
2526
}
2627

27-
const FormField: React.FC<Props> = ({ label, info, error, tip, children }) => {
28+
const FormField: React.FC<Props> = ({ label, info, error, tip, children, className }) => {
2829
const { Wrapper, Info } = Styled;
2930
return (
30-
<Wrapper>
31+
<Wrapper className={className}>
3132
{label && (
3233
<HeaderFour>
3334
{label}

app/src/components/common/FormInputNumber.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ interface Props {
77
value?: number;
88
extra?: ReactNode;
99
placeholder?: string;
10+
className?: string;
1011
onChange: (value: number) => void;
1112
}
1213

@@ -15,6 +16,7 @@ const FormInputNumber: React.FC<Props> = ({
1516
value,
1617
extra,
1718
placeholder,
19+
className,
1820
onChange,
1921
}) => {
2022
const handleChange = useCallback(
@@ -35,6 +37,7 @@ const FormInputNumber: React.FC<Props> = ({
3537

3638
return (
3739
<FormInput
40+
className={className}
3841
label={label}
3942
value={valueText}
4043
extra={extra}

0 commit comments

Comments
 (0)