summaryrefslogtreecommitdiffhomepage
path: root/app/components/Connect.js
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2018-01-29 14:47:52 +0100
committerAndrej Mihajlov <and@mullvad.net>2018-01-29 15:16:23 +0100
commit2fdadcd80b2425d6e82082e735de903c6a551d2e (patch)
tree98b10ea04a55542baf67c2cf74c60f1a1ab63099 /app/components/Connect.js
parentb818240e9fead04e8f5981a44a4237a6a5cd0619 (diff)
downloadmullvadvpn-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.js122
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