diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-10-29 13:11:34 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-10-29 13:11:34 +0100 |
| commit | 392b2a293e34807176adb3e997df3b89445232f0 (patch) | |
| tree | 7ccb9a6b35d9ef187aadba11ce8f0cfc66f9f1e1 | |
| parent | 02942bb74ad8efa6f1ef255bd0802288032cdb30 (diff) | |
| parent | c6c05176f1f5963d1771ac76068629aeaf8560c1 (diff) | |
| download | mullvadvpn-392b2a293e34807176adb3e997df3b89445232f0.tar.xz mullvadvpn-392b2a293e34807176adb3e997df3b89445232f0.zip | |
Merge branch 'improve-eslint-rules-2' into master
| -rw-r--r-- | gui/.eslintrc.js | 17 | ||||
| -rw-r--r-- | gui/src/main/daemon-rpc.ts | 318 | ||||
| -rw-r--r-- | gui/src/renderer/components/Accordion.tsx | 6 | ||||
| -rw-r--r-- | gui/src/renderer/components/AdvancedSettings.tsx | 8 | ||||
| -rw-r--r-- | gui/src/renderer/components/Map.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/components/Marquee.tsx | 6 | ||||
| -rw-r--r-- | gui/src/renderer/components/WireguardKeys.tsx | 4 |
7 files changed, 190 insertions, 173 deletions
diff --git a/gui/.eslintrc.js b/gui/.eslintrc.js index c2cd00afe7..3e12630d7b 100644 --- a/gui/.eslintrc.js +++ b/gui/.eslintrc.js @@ -33,6 +33,22 @@ const namingConvention = [ }, ]; +const memberOrdering = { + default: [ + 'public-field', + 'protected-field', + 'private-field', + + 'public-constructor', + 'protected-constructor', + 'private-constructor', + + 'public-method', + 'protected-method', + 'private-method', + ], +}; + module.exports = { env: { es6: true, @@ -71,6 +87,7 @@ module.exports = { '@typescript-eslint/require-await': 'error', '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-unused-expressions': 'error', + '@typescript-eslint/member-ordering': ['error', memberOrdering], 'no-return-await': 'error', 'react/jsx-no-bind': 'error', '@typescript-eslint/naming-convention': ['error', ...namingConvention], diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 78dae947a0..130651c200 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -120,14 +120,6 @@ type CallFunctionArgument<T, R> = | undefined; export class DaemonRpc { - constructor(connectionParams: string) { - this.client = (new ManagementServiceClient( - connectionParams, - grpc.credentials.createInsecure(), - this.channelOptions(), - ) as unknown) as managementInterface.ManagementServiceClient; - } - private client: managementInterface.ManagementServiceClient; private isConnected = false; private connectionObservers: ConnectionObserver[] = []; @@ -135,60 +127,12 @@ export class DaemonRpc { private subscriptions: Map<number, grpc.ClientReadableStream<grpcTypes.DaemonEvent>> = new Map(); private reconnectionTimeout?: number; - private subscriptionId(): number { - const current = this.nextSubscriptionId; - this.nextSubscriptionId += 1; - return current; - } - - private deadlineFromNow() { - return Date.now() + NETWORK_CALL_TIMEOUT; - } - - private channelStateTimeout(): number { - return Date.now() + CHANNEL_STATE_TIMEOUT; - } - - private callEmpty<R>(fn: CallFunctionArgument<Empty, R>): Promise<R> { - return this.call<Empty, R>(fn, new Empty()); - } - - private callString<R>(fn: CallFunctionArgument<StringValue, R>, value?: string): Promise<R> { - const googleString = new StringValue(); - - if (value !== undefined) { - googleString.setValue(value); - } - - return this.call<StringValue, R>(fn, googleString); - } - - private callBool<R>(fn: CallFunctionArgument<BoolValue, R>, value?: boolean): Promise<R> { - const googleBool = new BoolValue(); - - if (value !== undefined) { - googleBool.setValue(value); - } - - return this.call<BoolValue, R>(fn, googleBool); - } - - private callNumber<R>(fn: CallFunctionArgument<UInt32Value, R>, value?: number): Promise<R> { - const googleNumber = new UInt32Value(); - - if (value !== undefined) { - googleNumber.setValue(value); - } - - return this.call<UInt32Value, R>(fn, googleNumber); - } - - private call<T, R>(fn: CallFunctionArgument<T, R>, arg: T): Promise<R> { - if (fn && this.isConnected) { - return promisify<T, R>(fn.bind(this.client))(arg); - } else { - throw noConnectionError; - } + constructor(connectionParams: string) { + this.client = (new ManagementServiceClient( + connectionParams, + grpc.credentials.createInsecure(), + this.channelOptions(), + ) as unknown) as managementInterface.ManagementServiceClient; } public connect(): Promise<void> { @@ -209,87 +153,6 @@ export class DaemonRpc { }); } - private channelOptions(): grpc.ClientOptions { - /* eslint-disable @typescript-eslint/naming-convention */ - return { - 'grpc.max_reconnect_backoff_ms': 3000, - 'grpc.initial_reconnect_backoff_ms': 3000, - 'grpc.keepalive_time_ms': Math.pow(2, 30), - 'grpc.keepalive_timeout_ms': Math.pow(2, 30), - }; - /* eslint-enable @typescript-eslint/naming-convention */ - } - - private connectivityChangeCallback(timeoutErr?: Error) { - const channel = this.client.getChannel(); - const currentState = channel?.getConnectivityState(true); - log.debug(`GRPC Channel connectivity state changed to ${currentState}`); - if (channel) { - if (timeoutErr) { - this.setChannelCallback(currentState); - return; - } - const wasConnected = this.isConnected; - if (this.channelDisconnected(currentState)) { - this.connectionObservers.forEach((observer) => observer.onClose()); - this.isConnected = false; - // Try and reconnect in case - consumePromise( - this.connect().catch((error) => { - log.error(`Failed to reconnect - ${error}`); - }), - ); - this.setChannelCallback(currentState); - } else if (!wasConnected && currentState === grpc.connectivityState.READY) { - this.isConnected = true; - this.connectionObservers.forEach((observer) => observer.onOpen()); - this.setChannelCallback(currentState); - } - } - } - - private channelDisconnected(state: grpc.connectivityState): boolean { - return ( - (state === grpc.connectivityState.SHUTDOWN || - state === grpc.connectivityState.TRANSIENT_FAILURE || - state === grpc.connectivityState.IDLE) && - this.isConnected - ); - } - - private setChannelCallback(currentState?: grpc.connectivityState) { - const channel = this.client.getChannel(); - if (currentState === undefined && channel) { - currentState = channel?.getConnectivityState(false); - } - if (currentState) { - channel.watchConnectivityState(currentState, this.channelStateTimeout(), (error) => - this.connectivityChangeCallback(error), - ); - } - } - - // Since grpc.Channel.watchConnectivityState() isn't always running as intended, whenever the - // client fails to connect at first, `ensureConnectivity()` should be called so that it tries to - // check the connectivity state and nudge the client into connecting. - // `grpc.Channel.getConnectivityState(true)` should make it attempt to connect. - private ensureConnectivity() { - this.reconnectionTimeout = setTimeout(() => { - const lastState = this.client.getChannel().getConnectivityState(true); - if (this.channelDisconnected(lastState)) { - this.connectionObservers.forEach((observer) => observer.onClose()); - this.isConnected = false; - } - if (!this.isConnected) { - consumePromise( - this.connect().catch((error) => { - log.error(`Failed to reconnect - ${error}`); - }), - ); - } - }, 3000); - } - public disconnect() { this.isConnected = false; this.subscriptions.clear(); @@ -562,22 +425,6 @@ export class DaemonRpc { } } - private removeSubscription(id: number) { - const subscription = this.subscriptions.get(id); - if (subscription !== undefined) { - this.subscriptions.delete(id); - subscription.removeAllListeners('data'); - subscription.removeAllListeners('error'); - try { - subscription.cancel(); - } catch (error) { - if (error.code !== grpc.status.CANCELLED) { - throw error; - } - } - } - } - public async getAccountHistory(): Promise<AccountToken[]> { const response = await this.callEmpty<grpcTypes.AccountHistory>(this.client.getAccountHistory); return response.toObject().tokenList; @@ -614,6 +461,159 @@ export class DaemonRpc { const response = await this.callEmpty<grpcTypes.AppVersionInfo>(this.client.getVersionInfo); return response.toObject(); } + + private subscriptionId(): number { + const current = this.nextSubscriptionId; + this.nextSubscriptionId += 1; + return current; + } + + private deadlineFromNow() { + return Date.now() + NETWORK_CALL_TIMEOUT; + } + + private channelStateTimeout(): number { + return Date.now() + CHANNEL_STATE_TIMEOUT; + } + + private callEmpty<R>(fn: CallFunctionArgument<Empty, R>): Promise<R> { + return this.call<Empty, R>(fn, new Empty()); + } + + private callString<R>(fn: CallFunctionArgument<StringValue, R>, value?: string): Promise<R> { + const googleString = new StringValue(); + + if (value !== undefined) { + googleString.setValue(value); + } + + return this.call<StringValue, R>(fn, googleString); + } + + private callBool<R>(fn: CallFunctionArgument<BoolValue, R>, value?: boolean): Promise<R> { + const googleBool = new BoolValue(); + + if (value !== undefined) { + googleBool.setValue(value); + } + + return this.call<BoolValue, R>(fn, googleBool); + } + + private callNumber<R>(fn: CallFunctionArgument<UInt32Value, R>, value?: number): Promise<R> { + const googleNumber = new UInt32Value(); + + if (value !== undefined) { + googleNumber.setValue(value); + } + + return this.call<UInt32Value, R>(fn, googleNumber); + } + + private call<T, R>(fn: CallFunctionArgument<T, R>, arg: T): Promise<R> { + if (fn && this.isConnected) { + return promisify<T, R>(fn.bind(this.client))(arg); + } else { + throw noConnectionError; + } + } + + private removeSubscription(id: number) { + const subscription = this.subscriptions.get(id); + if (subscription !== undefined) { + this.subscriptions.delete(id); + subscription.removeAllListeners('data'); + subscription.removeAllListeners('error'); + try { + subscription.cancel(); + } catch (error) { + if (error.code !== grpc.status.CANCELLED) { + throw error; + } + } + } + } + + private channelOptions(): grpc.ClientOptions { + /* eslint-disable @typescript-eslint/naming-convention */ + return { + 'grpc.max_reconnect_backoff_ms': 3000, + 'grpc.initial_reconnect_backoff_ms': 3000, + 'grpc.keepalive_time_ms': Math.pow(2, 30), + 'grpc.keepalive_timeout_ms': Math.pow(2, 30), + }; + /* eslint-enable @typescript-eslint/naming-convention */ + } + + private connectivityChangeCallback(timeoutErr?: Error) { + const channel = this.client.getChannel(); + const currentState = channel?.getConnectivityState(true); + log.debug(`GRPC Channel connectivity state changed to ${currentState}`); + if (channel) { + if (timeoutErr) { + this.setChannelCallback(currentState); + return; + } + const wasConnected = this.isConnected; + if (this.channelDisconnected(currentState)) { + this.connectionObservers.forEach((observer) => observer.onClose()); + this.isConnected = false; + // Try and reconnect in case + consumePromise( + this.connect().catch((error) => { + log.error(`Failed to reconnect - ${error}`); + }), + ); + this.setChannelCallback(currentState); + } else if (!wasConnected && currentState === grpc.connectivityState.READY) { + this.isConnected = true; + this.connectionObservers.forEach((observer) => observer.onOpen()); + this.setChannelCallback(currentState); + } + } + } + + private channelDisconnected(state: grpc.connectivityState): boolean { + return ( + (state === grpc.connectivityState.SHUTDOWN || + state === grpc.connectivityState.TRANSIENT_FAILURE || + state === grpc.connectivityState.IDLE) && + this.isConnected + ); + } + + private setChannelCallback(currentState?: grpc.connectivityState) { + const channel = this.client.getChannel(); + if (currentState === undefined && channel) { + currentState = channel?.getConnectivityState(false); + } + if (currentState) { + channel.watchConnectivityState(currentState, this.channelStateTimeout(), (error) => + this.connectivityChangeCallback(error), + ); + } + } + + // Since grpc.Channel.watchConnectivityState() isn't always running as intended, whenever the + // client fails to connect at first, `ensureConnectivity()` should be called so that it tries to + // check the connectivity state and nudge the client into connecting. + // `grpc.Channel.getConnectivityState(true)` should make it attempt to connect. + private ensureConnectivity() { + this.reconnectionTimeout = setTimeout(() => { + const lastState = this.client.getChannel().getConnectivityState(true); + if (this.channelDisconnected(lastState)) { + this.connectionObservers.forEach((observer) => observer.onClose()); + this.isConnected = false; + } + if (!this.isConnected) { + consumePromise( + this.connect().catch((error) => { + log.error(`Failed to reconnect - ${error}`); + }), + ); + } + }, 3000); + } } function liftConstraint<T>(constraint: Constraint<T> | undefined): T | undefined { diff --git a/gui/src/renderer/components/Accordion.tsx b/gui/src/renderer/components/Accordion.tsx index 1a26d765cd..01c0733165 100644 --- a/gui/src/renderer/components/Accordion.tsx +++ b/gui/src/renderer/components/Accordion.tsx @@ -29,9 +29,6 @@ const Content = styled.div({ }); export default class Accordion extends React.Component<IProps, IState> { - private containerRef = React.createRef<HTMLDivElement>(); - private contentRef = React.createRef<HTMLDivElement>(); - public static defaultProps = { expanded: true, animationDuration: 350, @@ -42,6 +39,9 @@ export default class Accordion extends React.Component<IProps, IState> { containerHeight: this.props.expanded ? 'auto' : '0', }; + private containerRef = React.createRef<HTMLDivElement>(); + private contentRef = React.createRef<HTMLDivElement>(); + public componentDidUpdate(oldProps: IProps) { if (this.props.expanded && !oldProps.expanded) { this.expand(); diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx index b044226634..a34b5b499c 100644 --- a/gui/src/renderer/components/AdvancedSettings.tsx +++ b/gui/src/renderer/components/AdvancedSettings.tsx @@ -77,15 +77,15 @@ interface IState { } export default class AdvancedSettings extends React.Component<IProps, IState> { + public state = { + showConfirmBlockWhenDisconnectedAlert: false, + }; + private portItems: { [key in RelayProtocol]: Array<ISelectorItem<OptionalPort>> }; private protocolItems: Array<ISelectorItem<OptionalRelayProtocol>>; private bridgeStateItems: Array<ISelectorItem<BridgeState>>; private wireguardPortItems: Array<ISelectorItem<OptionalPort>>; - public state = { - showConfirmBlockWhenDisconnectedAlert: false, - }; - constructor(props: IProps) { super(props); diff --git a/gui/src/renderer/components/Map.tsx b/gui/src/renderer/components/Map.tsx index 8bc78b4b68..ddab985992 100644 --- a/gui/src/renderer/components/Map.tsx +++ b/gui/src/renderer/components/Map.tsx @@ -30,8 +30,6 @@ interface IState { } export default class Map extends React.Component<IProps, IState> { - private containerRef = React.createRef<HTMLDivElement>(); - public state: IState = { bounds: { width: 0, @@ -39,6 +37,8 @@ export default class Map extends React.Component<IProps, IState> { }, }; + private containerRef = React.createRef<HTMLDivElement>(); + public render() { const { width, height } = this.state.bounds; const readyToRenderTheMap = width > 0 && height > 0; diff --git a/gui/src/renderer/components/Marquee.tsx b/gui/src/renderer/components/Marquee.tsx index d2d5bcbccd..427ab81780 100644 --- a/gui/src/renderer/components/Marquee.tsx +++ b/gui/src/renderer/components/Marquee.tsx @@ -30,14 +30,14 @@ interface IMarqueeState { } export default class Marquee extends React.Component<IMarqueeProps, IMarqueeState> { - private textRef = React.createRef<HTMLSpanElement>(); - private scheduler = new Scheduler(); - public state = { alignRight: false, uniqueKey: 0, }; + private textRef = React.createRef<HTMLSpanElement>(); + private scheduler = new Scheduler(); + public componentDidMount() { this.startAnimationIfOverflow(); } diff --git a/gui/src/renderer/components/WireguardKeys.tsx b/gui/src/renderer/components/WireguardKeys.tsx index d5b0dc1833..9e1df9693c 100644 --- a/gui/src/renderer/components/WireguardKeys.tsx +++ b/gui/src/renderer/components/WireguardKeys.tsx @@ -53,14 +53,14 @@ export interface IState { } export default class WireguardKeys extends React.Component<IProps, IState> { - private keyAgeUpdateInterval?: number; - public state = { recentlyGeneratedKey: false, userHasInitiatedVerification: false, ageOfKeyString: WireguardKeys.ageOfKeyString(this.props.keyState, this.props.locale), }; + private keyAgeUpdateInterval?: number; + public static getDerivedStateFromProps(props: IProps) { return { ageOfKeyString: WireguardKeys.ageOfKeyString(props.keyState, props.locale), |
