Simplify custom node URL (#1141)
* Simplify custom nodes to just be a URL, not a url + port. * Allow modals to specify max width (#1142)
This commit is contained in:
parent
dc24a52e2c
commit
b48617e95e
|
@ -20,6 +20,9 @@ interface Input {
|
|||
name: string;
|
||||
placeholder?: string;
|
||||
type?: string;
|
||||
autoComplete?: 'off';
|
||||
onFocus?(): void;
|
||||
onBlur?(): void;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -40,7 +43,6 @@ interface StateProps {
|
|||
interface State {
|
||||
name: string;
|
||||
url: string;
|
||||
port: string;
|
||||
network: string;
|
||||
customNetworkId: string;
|
||||
customNetworkUnit: string;
|
||||
|
@ -56,7 +58,6 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
public state: State = {
|
||||
name: '',
|
||||
url: '',
|
||||
port: '',
|
||||
network: Object.keys(this.props.staticNetworks)[0],
|
||||
customNetworkId: '',
|
||||
customNetworkUnit: '',
|
||||
|
@ -94,6 +95,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
isOpen={true}
|
||||
buttons={buttons}
|
||||
handleClose={handleClose}
|
||||
maxWidth={580}
|
||||
>
|
||||
<div>
|
||||
{isHttps && <div className="alert alert-warning small">{translate('NODE_Warning')}</div>}
|
||||
|
@ -175,27 +177,14 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<hr />
|
||||
|
||||
<div className="row">
|
||||
<div className="col-sm-9">
|
||||
<div className="col-sm-12">
|
||||
<label>URL</label>
|
||||
{this.renderInput(
|
||||
{
|
||||
name: 'url',
|
||||
placeholder: 'https://127.0.0.1/'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-sm-3">
|
||||
<label>{translate('NODE_Port')}</label>
|
||||
{this.renderInput(
|
||||
{
|
||||
name: 'port',
|
||||
placeholder: '8545',
|
||||
type: 'number'
|
||||
placeholder: 'e.g. https://127.0.0.1:8545/',
|
||||
autoComplete: 'off'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
|
@ -248,6 +237,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
})}
|
||||
value={this.state[input.name]}
|
||||
onChange={this.handleChange}
|
||||
autoComplete="off"
|
||||
{...input}
|
||||
/>
|
||||
);
|
||||
|
@ -256,7 +246,6 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
private getInvalids(): { [key: string]: boolean } {
|
||||
const {
|
||||
url,
|
||||
port,
|
||||
hasAuth,
|
||||
username,
|
||||
password,
|
||||
|
@ -265,7 +254,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
customNetworkUnit,
|
||||
customNetworkChainId
|
||||
} = this.state;
|
||||
const required: (keyof State)[] = ['name', 'url', 'port', 'network'];
|
||||
const required: (keyof State)[] = ['name', 'url', 'network'];
|
||||
const invalids: { [key: string]: boolean } = {};
|
||||
|
||||
// Required fields
|
||||
|
@ -275,17 +264,12 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
}
|
||||
});
|
||||
|
||||
// Somewhat valid URL, not 100% fool-proof
|
||||
if (!/https?\:\/\/\w+/i.test(url)) {
|
||||
// Parse the URL, and make sure what they typed isn't parsed as relative.
|
||||
// Not a perfect regex, just checks for protocol + any char
|
||||
if (!/^https?:\/\/.+/i.test(url)) {
|
||||
invalids.url = true;
|
||||
}
|
||||
|
||||
// Numeric port within range
|
||||
const iport = parseInt(port, 10);
|
||||
if (!iport || iport < 1 || iport > 65535) {
|
||||
invalids.port = true;
|
||||
}
|
||||
|
||||
// If they have auth, make sure it's provided
|
||||
if (hasAuth) {
|
||||
if (!username) {
|
||||
|
@ -331,28 +315,25 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
private makeCustomNodeConfigFromState(): CustomNodeConfig {
|
||||
const { network } = this.state;
|
||||
const { network, url, name, username, password } = this.state;
|
||||
|
||||
const networkId =
|
||||
network === CUSTOM
|
||||
? this.makeCustomNetworkId(this.makeCustomNetworkConfigFromState())
|
||||
: network;
|
||||
|
||||
const port = parseInt(this.state.port, 10);
|
||||
const url = this.state.url.trim();
|
||||
const node: Omit<CustomNodeConfig, 'lib'> = {
|
||||
isCustom: true,
|
||||
service: 'your custom node',
|
||||
id: `${url}:${port}`,
|
||||
name: this.state.name.trim(),
|
||||
id: url,
|
||||
name: name.trim(),
|
||||
url,
|
||||
port,
|
||||
network: networkId,
|
||||
...(this.state.hasAuth
|
||||
? {
|
||||
auth: {
|
||||
username: this.state.username,
|
||||
password: this.state.password
|
||||
username,
|
||||
password
|
||||
}
|
||||
}
|
||||
: {})
|
||||
|
|
|
@ -103,23 +103,23 @@ class Header extends Component<Props, State> {
|
|||
const LanguageDropDown = Dropdown as new () => Dropdown<typeof selectedLanguage>;
|
||||
const options = nodeOptions.map(n => {
|
||||
if (n.isCustom) {
|
||||
const { name: { networkId, nodeId }, isCustom, id, ...rest } = n;
|
||||
const { label, isCustom, id, ...rest } = n;
|
||||
return {
|
||||
...rest,
|
||||
name: (
|
||||
<span>
|
||||
{networkId} - {nodeId} <small>(custom)</small>
|
||||
{label.network} - {label.nodeName} <small>(custom)</small>
|
||||
</span>
|
||||
),
|
||||
onRemove: () => this.props.removeCustomNode({ id })
|
||||
};
|
||||
} else {
|
||||
const { name: { networkId, service }, isCustom, ...rest } = n;
|
||||
const { label, isCustom, ...rest } = n;
|
||||
return {
|
||||
...rest,
|
||||
name: (
|
||||
<span>
|
||||
{networkId} <small>({service})</small>
|
||||
{label.network} <small>({label.service})</small>
|
||||
</span>
|
||||
)
|
||||
};
|
||||
|
|
|
@ -111,7 +111,7 @@ $m-anim-speed: 400ms;
|
|||
|
||||
// Mobile styles
|
||||
@media(max-width: $screen-sm) {
|
||||
width: calc(100% - 40px);
|
||||
width: calc(100% - 40px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,13 @@ interface Props {
|
|||
disableButtons?: boolean;
|
||||
children: any;
|
||||
buttons?: IButton[];
|
||||
maxWidth?: number;
|
||||
handleClose?(): void;
|
||||
}
|
||||
interface ModalStyle {
|
||||
width?: string;
|
||||
maxWidth?: string;
|
||||
}
|
||||
|
||||
const Fade = ({ children, ...props }) => (
|
||||
<CSSTransition {...props} timeout={300} classNames="animate-modal">
|
||||
|
@ -46,16 +51,22 @@ export default class Modal extends PureComponent<Props, {}> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { isOpen, title, children, buttons, handleClose } = this.props;
|
||||
const { isOpen, title, children, buttons, handleClose, maxWidth } = this.props;
|
||||
const hasButtons = buttons && buttons.length;
|
||||
const modalStyle: ModalStyle = {};
|
||||
|
||||
if (maxWidth) {
|
||||
modalStyle.width = '100%';
|
||||
modalStyle.maxWidth = `${maxWidth}px`;
|
||||
}
|
||||
|
||||
return (
|
||||
<TransitionGroup>
|
||||
{isOpen && (
|
||||
<Fade>
|
||||
<div>
|
||||
<div className={`Modalshade`} />
|
||||
<div className={`Modal`}>
|
||||
<div className="Modalshade" />
|
||||
<div className="Modal" style={modalStyle}>
|
||||
{title && (
|
||||
<div className="Modal-header flex-wrapper">
|
||||
<h2 className="Modal-header-title">{title}</h2>
|
||||
|
|
|
@ -119,7 +119,7 @@ export function getNodeLib(state: AppState) {
|
|||
export interface NodeOption {
|
||||
isCustom: false;
|
||||
value: string;
|
||||
name: { networkId?: string; service: string };
|
||||
label: { network: string; service: string };
|
||||
color?: string;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
@ -127,12 +127,14 @@ export interface NodeOption {
|
|||
export function getStaticNodeOptions(state: AppState): NodeOption[] {
|
||||
const staticNetworkConfigs = getStaticNetworkConfigs(state);
|
||||
return Object.entries(getStaticNodes(state)).map(([nodeId, node]: [string, StaticNodeConfig]) => {
|
||||
const networkId = node.network;
|
||||
const associatedNetwork = staticNetworkConfigs[networkId];
|
||||
const associatedNetwork = staticNetworkConfigs[node.network];
|
||||
const opt: NodeOption = {
|
||||
isCustom: node.isCustom,
|
||||
value: nodeId,
|
||||
name: { networkId, service: node.service },
|
||||
label: {
|
||||
network: node.network,
|
||||
service: node.service
|
||||
},
|
||||
color: associatedNetwork.color,
|
||||
hidden: node.hidden
|
||||
};
|
||||
|
@ -144,7 +146,10 @@ export interface CustomNodeOption {
|
|||
isCustom: true;
|
||||
id: string;
|
||||
value: string;
|
||||
name: { networkId?: string; nodeId: string };
|
||||
label: {
|
||||
network: string;
|
||||
nodeName: string;
|
||||
};
|
||||
color?: string;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
@ -153,15 +158,18 @@ export function getCustomNodeOptions(state: AppState): CustomNodeOption[] {
|
|||
const staticNetworkConfigs = getStaticNetworkConfigs(state);
|
||||
const customNetworkConfigs = getCustomNetworkConfigs(state);
|
||||
return Object.entries(getCustomNodeConfigs(state)).map(
|
||||
([nodeId, node]: [string, CustomNodeConfig]) => {
|
||||
const networkId = node.network;
|
||||
const associatedNetwork = isStaticNetworkId(state, networkId)
|
||||
? staticNetworkConfigs[networkId]
|
||||
: customNetworkConfigs[networkId];
|
||||
([_, node]: [string, CustomNodeConfig]) => {
|
||||
const chainId = node.network;
|
||||
const associatedNetwork = isStaticNetworkId(state, chainId)
|
||||
? staticNetworkConfigs[chainId]
|
||||
: customNetworkConfigs[chainId];
|
||||
const opt: CustomNodeOption = {
|
||||
isCustom: node.isCustom,
|
||||
value: node.id,
|
||||
name: { networkId, nodeId },
|
||||
label: {
|
||||
network: associatedNetwork.unit,
|
||||
nodeName: node.name
|
||||
},
|
||||
color: associatedNetwork.isCustom ? undefined : associatedNetwork.color,
|
||||
hidden: false,
|
||||
id: node.id
|
||||
|
|
|
@ -10,7 +10,6 @@ interface CustomNodeConfig {
|
|||
lib: CustomNode;
|
||||
service: 'your custom node';
|
||||
url: string;
|
||||
port: number;
|
||||
network: string;
|
||||
auth?: {
|
||||
username: string;
|
||||
|
|
|
@ -9,7 +9,6 @@ const firstCustomNode: CustomNodeConfig = {
|
|||
lib: jest.fn() as any,
|
||||
name: 'My cool custom node',
|
||||
network: 'CustomNetworkId',
|
||||
port: 8080,
|
||||
service: 'your custom node',
|
||||
url: '127.0.0.1'
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue