summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2017-12-19 12:34:24 +0100
committerAndrej Mihajlov <and@mullvad.net>2017-12-19 14:12:44 +0100
commit557595d2c4673004bf0e1218c8bbdc748c7c63b6 (patch)
tree5eedbc23b941f21bb189f4b3d127acaa97b0e90e
parentbbef28c9270aa199143098019546682cf6e3435d (diff)
downloadmullvadvpn-557595d2c4673004bf0e1218c8bbdc748c7c63b6.tar.xz
mullvadvpn-557595d2c4673004bf0e1218c8bbdc748c7c63b6.zip
Integrate RelayList into Redux
-rw-r--r--app/components/Connect.js2
-rw-r--r--app/components/SelectLocation.js38
-rw-r--r--app/lib/backend.js127
-rw-r--r--app/redux/settings/actions.js6
-rw-r--r--app/redux/settings/reducers.js22
-rw-r--r--test/components/Connect.spec.js23
-rw-r--r--test/components/SelectLocation.spec.js21
-rw-r--r--test/components/Settings.spec.js4
8 files changed, 137 insertions, 106 deletions
diff --git a/app/components/Connect.js b/app/components/Connect.js
index dc8e06c1da..93ac19a4eb 100644
--- a/app/components/Connect.js
+++ b/app/components/Connect.js
@@ -95,7 +95,7 @@ export default class Connect extends Component {
}
_findRelayName(relay: RelayLocation): ?string {
- const { countries } = this.props.settings.relayLocations;
+ const countries = this.props.settings.relayLocations;
const countryPredicate = (countryCode) => (country) => country.code === countryCode;
if(relay.country) {
diff --git a/app/components/SelectLocation.js b/app/components/SelectLocation.js
index ac00e305d4..7fb124bf33 100644
--- a/app/components/SelectLocation.js
+++ b/app/components/SelectLocation.js
@@ -8,8 +8,8 @@ import ChevronDownSVG from '../assets/images/icon-chevron-down.svg';
import ChevronUpSVG from '../assets/images/icon-chevron-up.svg';
import TickSVG from '../assets/images/icon-tick.svg';
-import type { SettingsReduxState } from '../redux/settings/reducers';
-import type { RelayLocation, RelayListCity, RelayListCountry } from '../lib/ipc-facade';
+import type { SettingsReduxState, RelayLocationRedux, RelayLocationCityRedux } from '../redux/settings/reducers';
+import type { RelayLocation } from '../lib/ipc-facade';
export type SelectLocationProps = {
settings: SettingsReduxState,
@@ -73,7 +73,7 @@ export default class SelectLocation extends Component {
While connected, your real location is masked with a private and secure location in the selected region
</div>
- { this.props.settings.relayLocations.countries.map((relayCountry) => {
+ { this.props.settings.relayLocations.map((relayCountry) => {
return this._renderCountry(relayCountry);
}) }
@@ -126,17 +126,13 @@ export default class SelectLocation extends Component {
return (<div className={ 'select-location-relay-status ' + statusClass }></div>);
}
- _renderCountry(relayCountry: RelayListCountry) {
- const countryHasActiveRelays = relayCountry.cities.some((relayCity) => {
- return relayCity.has_active_relays;
- });
-
+ _renderCountry(relayCountry: RelayLocationRedux) {
const isSelected = this._isSelected({ country: relayCountry.code });
// either expanded by user or when the city selected within the country
const isExpanded = this.state.expanded.includes(relayCountry.code);
- const handleSelect = (countryHasActiveRelays && !isSelected) ? () => {
+ const handleSelect = (relayCountry.hasActiveRelays && !isSelected) ? () => {
this.props.onSelect({ country: relayCountry.code });
} : undefined;
@@ -147,7 +143,7 @@ export default class SelectLocation extends Component {
const countryClass = 'select-location__cell' +
(isSelected ? ' select-location__cell--selected' : '') +
- (countryHasActiveRelays ? ' select-location__cell--selectable' : '');
+ (relayCountry.hasActiveRelays ? ' select-location__cell--selectable' : '');
const onRef = isSelected ? (element) => {
this._selectedCell = element;
@@ -163,16 +159,16 @@ export default class SelectLocation extends Component {
<div className="select-location__cell-icon">
{ isSelected ?
<TickSVG /> :
- this._relayStatusIndicator(countryHasActiveRelays) }
+ this._relayStatusIndicator(relayCountry.hasActiveRelays) }
</div>
<div className={ 'select-location__cell-label' +
- (countryHasActiveRelays ? '' : ' select-location__cell-label--inactive') }>
+ (relayCountry.hasActiveRelays ? '' : ' select-location__cell-label--inactive') }>
{ relayCountry.name }
</div>
</div>
- { countryHasActiveRelays && <button type="button" className="select-location__collapse-button" onClick={ handleCollapse }>
+ { relayCountry.hasActiveRelays && <button type="button" className="select-location__collapse-button" onClick={ handleCollapse }>
{ isExpanded ?
<ChevronUpSVG className="select-location__collapse-icon" /> :
<ChevronDownSVG className="select-location__collapse-icon" /> }
@@ -180,7 +176,7 @@ export default class SelectLocation extends Component {
</div>
- { countryHasActiveRelays && relayCountry.cities.length > 0 &&
+ { relayCountry.hasActiveRelays && relayCountry.cities.length > 0 &&
(<Accordion className="select-location__cities" height={ isExpanded ? 'auto' : 0 }>
{ relayCountry.cities.map((relayCity) => this._renderCity(relayCountry.code, relayCity)) }
</Accordion>)
@@ -189,18 +185,16 @@ export default class SelectLocation extends Component {
);
}
- _renderCity(countryCode: string, relayCity: RelayListCity) {
+ _renderCity(countryCode: string, relayCity: RelayLocationCityRedux) {
const relayLocation: RelayLocation = { city: [countryCode, relayCity.code] };
const isSelected = this._isSelected(relayLocation);
- const cityHasActiveRelays = relayCity.has_active_relays;
- const key = countryCode + '_' + relayCity.code;
const cityClass = 'select-location__sub-cell' +
(isSelected ? ' select-location__sub-cell--selected' : '') +
- (cityHasActiveRelays ? ' select-location__sub-cell--selectable' : '');
+ (relayCity.hasActiveRelays ? ' select-location__sub-cell--selectable' : '');
- const handleSelect = (cityHasActiveRelays && !isSelected) ? () => {
+ const handleSelect = (relayCity.hasActiveRelays && !isSelected) ? () => {
this.props.onSelect(relayLocation);
} : undefined;
@@ -209,7 +203,7 @@ export default class SelectLocation extends Component {
} : undefined;
return (
- <div key={ key }
+ <div key={ `${countryCode}_${relayCity.code}` }
className={ cityClass }
onClick={ handleSelect }
ref={ onRef }>
@@ -217,11 +211,11 @@ export default class SelectLocation extends Component {
<div className="select-location__cell-icon">
{ isSelected ?
<TickSVG /> :
- this._relayStatusIndicator(cityHasActiveRelays) }
+ this._relayStatusIndicator(relayCity.hasActiveRelays) }
</div>
<div className={ 'select-location__cell-label' +
- (cityHasActiveRelays ? '' : ' select-location__cell-label--inactive') }>
+ (relayCity.hasActiveRelays ? '' : ' select-location__cell-label--inactive') }>
{ relayCity.name }
</div>
</div>
diff --git a/app/lib/backend.js b/app/lib/backend.js
index 5fe9421648..f78d359926 100644
--- a/app/lib/backend.js
+++ b/app/lib/backend.js
@@ -121,54 +121,28 @@ export class Backend {
async sync() {
log.info('Syncing with the backend...');
- await this._ensureAuthenticated();
-
try {
- const locations = await this._ipc.getRelayLocations();
-
- log.info('Got relay locations');
-
- this._store.dispatch(
- settingsActions.updateRelayLocations(locations)
- );
- } catch (e) {
- log.error('Cannot fetch relay locations', e);
+ await this._fetchRelayLocations();
+ } catch(e) {
+ log.error('Failed to fetch the relay locations: ', e.message);
}
try {
- const publicIp = await this._ipc.getPublicIp();
-
- log.info('Got public IP: ', publicIp);
-
- this._store.dispatch(
- connectionActions.newPublicIp(publicIp)
- );
- } catch (e) {
- log.info('Cannot fetch public IP: ', e.message);
+ await this._fetchPublicIP();
+ } catch(e) {
+ log.error('Failed to fetch the public IP: ', e.message);
}
try {
- const location = await this._ipc.getLocation();
-
- log.info('Got location: ', location);
-
- const locationUpdate = {
- country: location.country,
- city: location.city,
- location: location.position
- };
-
- this._store.dispatch(
- connectionActions.newLocation(locationUpdate)
- );
- } catch (e) {
- log.info('Cannot fetch new location: ', e.message);
+ await this._fetchLocation();
+ } catch(e) {
+ log.error('Failed to fetch the location: ', e.message);
}
- await this._updateAccountHistory();
+ await this._fetchAccountHistory();
}
- async login(accountToken: AccountToken): Promise<void> {
+ async login(accountToken: AccountToken) {
log.debug('Attempting to login');
this._store.dispatch(accountActions.startLogin(accountToken));
@@ -197,7 +171,7 @@ export class Backend {
this.connect();
}, 1000);
- await this._updateAccountHistory();
+ await this._fetchAccountHistory();
} catch(e) {
log.error('Failed to log in,', e.message);
@@ -256,7 +230,7 @@ export class Backend {
}
}
- async connect(): Promise<void> {
+ async connect() {
try {
this._store.dispatch(connectionActions.connecting());
@@ -268,7 +242,7 @@ export class Backend {
}
}
- async disconnect(): Promise<void> {
+ async disconnect() {
// @TODO: Failure modes
try {
await this._ensureAuthenticated();
@@ -278,7 +252,7 @@ export class Backend {
}
}
- async shutdown(): Promise<void> {
+ async shutdown() {
try {
await this._ensureAuthenticated();
await this._ipc.shutdown();
@@ -287,7 +261,7 @@ export class Backend {
}
}
- async updateRelaySettings(relaySettings: RelaySettingsUpdate): Promise<void> {
+ async updateRelaySettings(relaySettings: RelaySettingsUpdate) {
try {
await this._ensureAuthenticated();
await this._ipc.updateRelaySettings(relaySettings);
@@ -296,7 +270,7 @@ export class Backend {
}
}
- async fetchRelaySettings(): Promise<void> {
+ async fetchRelaySettings() {
await this._ensureAuthenticated();
const relaySettings = await this._ipc.getRelaySettings();
@@ -342,17 +316,17 @@ export class Backend {
}
}
- async removeAccountFromHistory(accountToken: AccountToken): Promise<void> {
+ async removeAccountFromHistory(accountToken: AccountToken) {
try {
await this._ensureAuthenticated();
await this._ipc.removeAccountFromHistory(accountToken);
- await this._updateAccountHistory();
+ await this._fetchAccountHistory();
} catch(e) {
log.error('Failed to remove account token from history', e.message);
}
}
- async _updateAccountHistory(): Promise<void> {
+ async _fetchAccountHistory() {
try {
await this._ensureAuthenticated();
const accountHistory = await this._ipc.getAccountHistory();
@@ -361,9 +335,66 @@ export class Backend {
);
} catch(e) {
log.info('Failed to fetch account history,', e.message);
+ throw e;
}
}
+
+
+ async _fetchRelayLocations() {
+ await this._ensureAuthenticated();
+
+ const locations = await this._ipc.getRelayLocations();
+
+ log.info('Got relay locations');
+
+ const storedLocations = locations.countries.map((country) => ({
+ name: country.name,
+ code: country.code,
+ hasActiveRelays: country.cities.some((city) => city.has_active_relays),
+ cities: country.cities.map((city) => ({
+ name: city.name,
+ code: city.code,
+ position: city.position,
+ hasActiveRelays: city.has_active_relays,
+ }))
+ }));
+
+ this._store.dispatch(
+ settingsActions.updateRelayLocations(storedLocations)
+ );
+ }
+
+ async _fetchPublicIP() {
+ await this._ensureAuthenticated();
+
+ const publicIp = await this._ipc.getPublicIp();
+
+ log.info('Got public IP: ', publicIp);
+
+ this._store.dispatch(
+ connectionActions.newPublicIp(publicIp)
+ );
+ }
+
+ async _fetchLocation() {
+ await this._ensureAuthenticated();
+
+ const location = await this._ipc.getLocation();
+
+ log.info('Got location: ', location);
+
+ const locationUpdate = {
+ country: location.country,
+ city: location.city,
+ location: location.position
+ };
+
+ this._store.dispatch(
+ connectionActions.newLocation(locationUpdate)
+ );
+ }
+
/**
* Start reachability monitoring for online/offline detection
* This is currently done via HTML5 APIs but will be replaced later
@@ -421,7 +452,7 @@ export class Backend {
throw new Error('Unsupported state/target state combination: ' + JSON.stringify(backendState));
}
- _ensureAuthenticated(): Promise<void> {
+ _ensureAuthenticated() {
const credentials = this._credentials;
if(credentials) {
if(!this._authenticationPromise) {
@@ -433,7 +464,7 @@ export class Backend {
}
}
- async _authenticate(sharedSecret: string): Promise<void> {
+ async _authenticate(sharedSecret: string) {
try {
await this._ipc.authenticate(sharedSecret);
log.info('Authenticated with backend');
diff --git a/app/redux/settings/actions.js b/app/redux/settings/actions.js
index c4f7b20efe..783eb042d2 100644
--- a/app/redux/settings/actions.js
+++ b/app/redux/settings/actions.js
@@ -1,6 +1,6 @@
// @flow
-import type { RelaySettingsRedux, RelayLocationsRedux } from './reducers';
+import type { RelaySettingsRedux, RelayLocationRedux } from './reducers';
export type UpdateRelayAction = {
type: 'UPDATE_RELAY',
@@ -9,7 +9,7 @@ export type UpdateRelayAction = {
export type UpdateRelayLocationsAction = {
type: 'UPDATE_RELAY_LOCATIONS',
- relayLocations: RelayLocationsRedux
+ relayLocations: Array<RelayLocationRedux>
}
export type SettingsAction = UpdateRelayAction | UpdateRelayLocationsAction;
@@ -21,7 +21,7 @@ function updateRelay(relay: RelaySettingsRedux): UpdateRelayAction {
};
}
-function updateRelayLocations(relayLocations: RelayLocationsRedux): UpdateRelayLocationsAction {
+function updateRelayLocations(relayLocations: Array<RelayLocationRedux>): UpdateRelayLocationsAction {
return {
type: 'UPDATE_RELAY_LOCATIONS',
relayLocations: relayLocations,
diff --git a/app/redux/settings/reducers.js b/app/redux/settings/reducers.js
index 779a6306ad..21258a80a7 100644
--- a/app/redux/settings/reducers.js
+++ b/app/redux/settings/reducers.js
@@ -1,7 +1,7 @@
// @flow
import type { ReduxAction } from '../store';
-import type { RelayProtocol, RelayLocation, RelayList } from '../../lib/ipc-facade';
+import type { RelayProtocol, RelayLocation } from '../../lib/ipc-facade';
export type RelaySettingsRedux = {|
normal: {
@@ -17,11 +17,23 @@ export type RelaySettingsRedux = {|
}
|};
-export type RelayLocationsRedux = RelayList;
+export type RelayLocationCityRedux = {
+ name: string,
+ code: string,
+ position: [number, number],
+ hasActiveRelays: boolean,
+};
+
+export type RelayLocationRedux = {
+ name: string,
+ code: string,
+ hasActiveRelays: boolean,
+ cities: Array<RelayLocationCityRedux>,
+};
export type SettingsReduxState = {
relaySettings: RelaySettingsRedux,
- relayLocations: RelayLocationsRedux,
+ relayLocations: Array<RelayLocationRedux>,
};
const initialState: SettingsReduxState = {
@@ -32,9 +44,7 @@ const initialState: SettingsReduxState = {
protocol: 'any',
}
},
- relayLocations: {
- countries: []
- },
+ relayLocations: [],
};
export default function(state: SettingsReduxState = initialState, action: ReduxAction): SettingsReduxState {
diff --git a/test/components/Connect.spec.js b/test/components/Connect.spec.js
index 799692466f..4fcc3c6f60 100644
--- a/test/components/Connect.spec.js
+++ b/test/components/Connect.spec.js
@@ -150,18 +150,17 @@ const defaultProps: ConnectProps = {
port: 'any',
}
},
- relayLocations: {
- countries: [{
- name: 'Sweden',
- code: 'se',
- cities: [{
- name: 'Malmö',
- code: 'mma',
- has_active_relays: true,
- position: [0, 0],
- }]
- }],
- },
+ relayLocations: [{
+ name: 'Sweden',
+ code: 'se',
+ hasActiveRelays: true,
+ cities: [{
+ name: 'Malmö',
+ code: 'mma',
+ hasActiveRelays: true,
+ position: [0, 0],
+ }]
+ }],
},
connection: {
status: 'disconnected',
diff --git a/test/components/SelectLocation.spec.js b/test/components/SelectLocation.spec.js
index a33112d43b..1abdf515d9 100644
--- a/test/components/SelectLocation.spec.js
+++ b/test/components/SelectLocation.spec.js
@@ -17,18 +17,17 @@ describe('components/SelectLocation', () => {
port: 'any',
}
},
- relayLocations: {
- countries: [{
- name: 'Sweden',
- code: 'se',
- cities: [{
- name: 'Malmö',
- code: 'mma',
- position: [0, 0],
- has_active_relays: true,
- }],
+ relayLocations: [{
+ name: 'Sweden',
+ code: 'se',
+ hasActiveRelays: true,
+ cities: [{
+ name: 'Malmö',
+ code: 'mma',
+ position: [0, 0],
+ hasActiveRelays: true,
}],
- },
+ }],
};
const makeProps = (state: SettingsReduxState, mergeProps: $Shape<SelectLocationProps>): SelectLocationProps => {
diff --git a/test/components/Settings.spec.js b/test/components/Settings.spec.js
index 7b4780ece2..cc38c54616 100644
--- a/test/components/Settings.spec.js
+++ b/test/components/Settings.spec.js
@@ -42,9 +42,7 @@ describe('components/Settings', () => {
port: 1301,
},
},
- relayLocations: {
- countries: [],
- },
+ relayLocations: [],
};
const makeProps = (anAccountState: AccountReduxState, aSettingsState: SettingsReduxState, mergeProps: $Shape<SettingsProps> = {}): SettingsProps => {