summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--app/components/Connect.js122
-rw-r--r--app/containers/ConnectPage.js37
-rw-r--r--test/components/Connect.spec.js45
3 files changed, 92 insertions, 112 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
diff --git a/app/containers/ConnectPage.js b/app/containers/ConnectPage.js
index d9f3bd81e7..9ce245a927 100644
--- a/app/containers/ConnectPage.js
+++ b/app/containers/ConnectPage.js
@@ -11,11 +11,46 @@ import { openLink } from '../lib/platform';
import type { ReduxState, ReduxDispatch } from '../redux/store';
import type { SharedRouteProps } from '../routes';
+import type { RelaySettingsRedux, RelayLocationRedux } from '../redux/settings/reducers';
+
+function getRelayName(relaySettings: RelaySettingsRedux, relayLocations: Array<RelayLocationRedux>): string {
+ if(relaySettings.normal) {
+ const location = relaySettings.normal.location;
+
+ if(location === 'any') {
+ return 'Automatic';
+ } else if(location.country) {
+ const country = relayLocations.find(({ code }) => code === location.country);
+ if(country) {
+ return country.name;
+ }
+ } else if(location.city) {
+ const [countryCode, cityCode] = location.city;
+ const country = relayLocations.find(({ code }) => code === countryCode);
+ if(country) {
+ const city = country.cities.find(({ code }) => code === cityCode);
+ if(city) {
+ return city.name;
+ }
+ }
+ }
+
+ return 'Unknown';
+ } else if(relaySettings.custom_tunnel_endpoint) {
+ return 'Custom';
+ } else {
+ throw new Error('Unsupported relay settings.');
+ }
+}
+
const mapStateToProps = (state: ReduxState) => {
return {
accountExpiry: state.account.expiry,
+ selectedRelayName: getRelayName(
+ state.settings.relaySettings,
+ state.settings.relayLocations
+ ),
connection: state.connection,
- settings: state.settings,
};
};
diff --git a/test/components/Connect.spec.js b/test/components/Connect.spec.js
index c92baf51d4..9c6ed0d109 100644
--- a/test/components/Connect.spec.js
+++ b/test/components/Connect.spec.js
@@ -98,28 +98,6 @@ describe('components/Connect', () => {
expect(ipAddr.text()).to.contain('4.3.2.1');
});
- it('shows the country name in the location switcher', () => {
- const component = renderWithProps({
- connection: {
- ...defaultProps.connection,
- status: 'disconnected',
- },
- settings: {
- ...defaultProps.settings,
- relaySettings: {
- normal: {
- location: { city: ['se', 'mma'] },
- protocol: 'any',
- port: 'any',
- }
- },
- },
- });
-
- const locationSwitcher = component.find('.connect__server');
- expect(locationSwitcher.text()).to.contain('Malmö');
- });
-
it('invokes the onConnect prop', (done) => {
const component = renderWithProps({
onConnect: () => done(),
@@ -142,28 +120,7 @@ const defaultProps: ConnectProps = {
onDisconnect: () => {},
onExternalLink: () => {},
accountExpiry: '',
- settings: {
- relaySettings: {
- normal: {
- location: 'any',
- protocol: 'any',
- port: 'any',
- }
- },
- relayLocations: [{
- name: 'Sweden',
- code: 'se',
- hasActiveRelays: true,
- cities: [{
- name: 'Malmö',
- code: 'mma',
- latitude: 0,
- longitude: 0,
- hasActiveRelays: true,
- }]
- }],
- allowLan: false,
- },
+ selectedRelayName: '',
connection: {
status: 'disconnected',
isOnline: true,