diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2018-01-29 14:47:52 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2018-01-29 15:16:23 +0100 |
| commit | 2fdadcd80b2425d6e82082e735de903c6a551d2e (patch) | |
| tree | 98b10ea04a55542baf67c2cf74c60f1a1ab63099 /app/components/Connect.js | |
| parent | b818240e9fead04e8f5981a44a4237a6a5cd0619 (diff) | |
| download | mullvadvpn-2fdadcd80b2425d6e82082e735de903c6a551d2e.tar.xz mullvadvpn-2fdadcd80b2425d6e82082e735de903c6a551d2e.zip | |
Calculate map offset dynamically and refine the Connect.state
Diffstat (limited to 'app/components/Connect.js')
| -rw-r--r-- | app/components/Connect.js | 122 |
1 files changed, 55 insertions, 67 deletions
diff --git a/app/components/Connect.js b/app/components/Connect.js index 83caaa0299..d29ac57c44 100644 --- a/app/components/Connect.js +++ b/app/components/Connect.js @@ -11,13 +11,11 @@ import ChevronRightSVG from '../assets/images/icon-chevron.svg'; import type { HeaderBarStyle } from './HeaderBar'; import type { ConnectionReduxState } from '../redux/connection/reducers'; -import type { SettingsReduxState } from '../redux/settings/reducers'; -import type { RelayLocation } from '../lib/ipc-facade'; export type ConnectProps = { - accountExpiry: string, connection: ConnectionReduxState, - settings: SettingsReduxState, + accountExpiry: string, + selectedRelayName: string, onSettings: () => void, onSelectLocation: () => void, onConnect: () => void, @@ -26,15 +24,37 @@ export type ConnectProps = { onExternalLink: (type: string) => void, }; +type ConnectState = { + showCopyIPMessage: boolean, + mapOffset: [number, number], +}; export default class Connect extends Component { props: ConnectProps; - state = { - showCopyIPMessage: false + state: ConnectState = { + showCopyIPMessage: false, + mapOffset: [0, 0], }; _copyTimer: ?number; + shouldComponentUpdate(nextProps: ConnectProps, nextState: ConnectState) { + const { connection: prevConnection, ...otherPrevProps } = this.props; + const { connection: nextConnection, ...otherNextProps } = nextProps; + + const prevState = this.state; + + return ( + // shallow compare the connection + !shallowCompare(prevConnection, nextConnection) || + !shallowCompare(otherPrevProps, otherNextProps) || + + prevState.mapOffset[0] !== nextState.mapOffset[0] || + prevState.mapOffset[1] !== nextState.mapOffset[1] || + prevState.showCopyIPMessage !== nextState.showCopyIPMessage + ); + } + componentWillUnmount() { if(this._copyTimer) { clearTimeout(this._copyTimer); @@ -46,7 +66,7 @@ export default class Connect extends Component { }); } - render(): React.Element<*> { + render() { const error = this.displayError(); const child = error ? this.renderError(error) : this.renderMap(); @@ -60,7 +80,7 @@ export default class Connect extends Component { ); } - renderError(error: BackendError): React.Element<*> { + renderError(error: BackendError) { return ( <div className="connect"> <div className="connect__status"> @@ -87,86 +107,46 @@ export default class Connect extends Component { ); } - _findRelayName(relay: RelayLocation): ?string { - const countries = this.props.settings.relayLocations; - const countryPredicate = (countryCode) => (country) => country.code === countryCode; - - if(relay.country) { - const country = countries.find(countryPredicate(relay.country)); - if(country) { - return country.name; - } - } else if(relay.city) { - const [countryCode, cityCode] = relay.city; - const country = countries.find(countryPredicate(countryCode)); - if(country) { - const city = country.cities.find((city) => city.code === cityCode); - if(city) { - return city.name; - } - } - } - return null; - } - - _getLocationName(): string { - const { relaySettings } = this.props.settings; - if(relaySettings.normal) { - const location = relaySettings.normal.location; - if(location === 'any') { - return 'Automatic'; - } else { - return this._findRelayName(location) || 'Unknown'; - } - } else if(relaySettings.custom_tunnel_endpoint) { - return 'Custom'; - } else { - throw new Error('Unsupported relay settings.'); - } - } - _getMapProps() { const { longitude, latitude, status } = this.props.connection; - const defaultProps = { - width: 320, - height: 429, - markerImagePath: status === 'connected' ? - './assets/images/location-marker-secure.svg' : - './assets/images/location-marker-unsecure.svg' - }; // when the user location is known if(typeof(longitude) === 'number' && typeof(latitude) === 'number') { return { - ...defaultProps, - center: [longitude, latitude], - // do not show the marker when connecting showMarker: status !== 'connecting', - + markerStyle: status === 'connected' ? 'secure' : 'unsecure', // zoom in when connected - zoomLevel: status === 'connected' ? 40 : 20, - + zoomLevel: status === 'connected' ? 'low' : 'medium', // a magic offset to align marker with spinner offset: [0, 123], }; } else { return { - ...defaultProps, - center: [0, 0], showMarker: false, - + markerStyle: 'unsecure', // show the world when user location is not known - zoomLevel: 1, - + zoomLevel: 'high', // remove the offset since the marker is hidden offset: [0, 0], }; } } + _updateMapOffset = (spinnerNode: HTMLElement) => { + if(spinnerNode) { + // calculate the vertical offset from the center of the map + // to shift the center of the map upwards to align the centers + // of spinner and marker on the map + const y = spinnerNode.offsetTop + spinnerNode.clientHeight * 0.5; + this.setState({ + mapOffset: [0, y] + }); + } + } + renderMap() { let [ isConnecting, isConnected, isDisconnected ] = [false, false, false]; switch(this.props.connection.status) { @@ -178,7 +158,7 @@ export default class Connect extends Component { return ( <div className="connect"> <div className="connect__map"> - <Map { ...this._getMapProps() } /> + <Map style={{ width: '100%', height: '100%' }} { ...this._getMapProps() } /> </div> <div className="connect__container"> @@ -186,7 +166,7 @@ export default class Connect extends Component { <div className="connect__status"> { /* show spinner when connecting */ } <div className={ this.spinnerClass() }> - <img src="./assets/images/icon-spinner.svg" alt="" /> + <img src="./assets/images/icon-spinner.svg" alt="" ref={ this._updateMapOffset } /> </div> <div className={ this.networkSecurityClass() }>{ this.networkSecurityMessage() }</div> @@ -243,7 +223,7 @@ export default class Connect extends Component { <div className="connect__footer"> <div className="connect__row"> <button className="connect__server button button--neutral button--blur" onClick={ this.props.onSelectLocation }> - <div className="connect__server-label">{ this._getLocationName() }</div> + <div className="connect__server-label">{ this.props.selectedRelayName }</div> <div className="connect__server-chevron"><ChevronRightSVG /></div> </button> </div> @@ -384,3 +364,11 @@ export default class Connect extends Component { return null; } } + +function shallowCompare(lhs: Object, rhs: Object) { + const keys = Object.keys(lhs); + return ( + keys.length === Object.keys(rhs).length && + keys.every(key => lhs[key] === rhs[key]) + ); +}
\ No newline at end of file |
