diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2018-09-05 18:39:51 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2018-09-05 18:39:51 +0200 |
| commit | 619e081cb90c3cf6b49c51e81ea6f6c3fbe1d9a9 (patch) | |
| tree | 5a5601eddbc639aa80808f80bf184ab97fe00220 /gui | |
| parent | fb40dedf46348e8764aed95610b135e40c3b1ba1 (diff) | |
| parent | c3eeeaaa9db90bbf4c6328286854e850d2f8156b (diff) | |
| download | mullvadvpn-619e081cb90c3cf6b49c51e81ea6f6c3fbe1d9a9.tar.xz mullvadvpn-619e081cb90c3cf6b49c51e81ea6f6c3fbe1d9a9.zip | |
Merge branch 'select-individual-servers'
Diffstat (limited to 'gui')
8 files changed, 173 insertions, 11 deletions
diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js index 8c170c1446..6cf203f4cb 100644 --- a/gui/packages/desktop/src/renderer/app.js +++ b/gui/packages/desktop/src/renderer/app.js @@ -346,6 +346,7 @@ export default class AppRenderer { latitude: city.latitude, longitude: city.longitude, hasActiveRelays: city.has_active_relays, + relays: city.relays, })), })); diff --git a/gui/packages/desktop/src/renderer/components/SelectLocation.js b/gui/packages/desktop/src/renderer/components/SelectLocation.js index 70e2bed978..ee1bb13c2c 100644 --- a/gui/packages/desktop/src/renderer/components/SelectLocation.js +++ b/gui/packages/desktop/src/renderer/components/SelectLocation.js @@ -15,6 +15,7 @@ import type { SettingsReduxState, RelayLocationRedux, RelayLocationCityRedux, + RelayLocationRelayRedux, } from '../redux/settings/reducers'; import type { RelayLocation } from '../lib/daemon-rpc'; @@ -48,7 +49,15 @@ export default class SelectLocation extends React.Component<SelectLocationProps, } else if (location.country) { this.state.expanded.push(location.country); } else if (location.city) { - this.state.expanded.push(location.city[0]); + const countryCode = location.city[0]; + + this.state.expanded.push(countryCode); + } else if (location.hostname) { + const countryCode = location.hostname[0]; + const cityCode = location.hostname[1]; + + this.state.expanded.push(countryCode); + this.state.expanded.push(`${countryCode}_${cityCode}`); } } } @@ -124,6 +133,16 @@ export default class SelectLocation extends React.Component<SelectLocationProps, selectedCity.every((v, i) => v === otherCity[i]) ); } + + if (Array.isArray(selectedLocation.hostname) && Array.isArray(otherLocation.hostname)) { + const selectedRelay = selectedLocation.hostname; + const otherRelay = otherLocation.hostname; + + return ( + selectedRelay.length === otherRelay.length && + selectedRelay.every((v, i) => v === otherRelay[i]) + ); + } } return false; } @@ -210,6 +229,7 @@ export default class SelectLocation extends React.Component<SelectLocationProps, } _renderCity(countryCode: string, relayCity: RelayLocationCityRedux) { + const expandedCode = `${countryCode}_${relayCity.code}`; const relayLocation: RelayLocation = { city: [countryCode, relayCity.code] }; const isSelected = this._isSelected(relayLocation); @@ -220,6 +240,9 @@ export default class SelectLocation extends React.Component<SelectLocationProps, } : undefined; + // either expanded by user or when the city or a relay from the city is selected + const isExpanded = this.state.expanded.includes(expandedCode); + const handleSelect = relayCity.hasActiveRelays && !isSelected ? () => { @@ -227,18 +250,73 @@ export default class SelectLocation extends React.Component<SelectLocationProps, } : undefined; + const handleCollapse = (e) => { + this._toggleCollapse(expandedCode); + e.stopPropagation(); + }; + + return ( + <View key={expandedCode}> + <Cell.CellButton + onPress={handleSelect} + disabled={!relayCity.hasActiveRelays} + cellHoverStyle={isSelected ? styles.sub_cell__selected : null} + style={isSelected ? styles.sub_cell__selected : styles.sub_cell} + testName="city" + ref={onRef}> + {this._relayStatusIndicator(relayCity.hasActiveRelays, isSelected)} + + <Cell.Label>{relayCity.name}</Cell.Label> + + {relayCity.relays.length > 1 ? ( + <Cell.Img + style={styles.collapse_button} + hoverStyle={styles.expand_chevron_hover} + onPress={handleCollapse} + source={isExpanded ? 'icon-chevron-up' : 'icon-chevron-down'} + height={24} + width={24} + /> + ) : null} + </Cell.CellButton> + + {relayCity.relays.length > 1 && ( + <Accordion height={isExpanded ? 'auto' : 0}> + {relayCity.relays.map((relay) => this._renderRelay(countryCode, relayCity.code, relay))} + </Accordion> + )} + </View> + ); + } + + _renderRelay(countryCode: string, cityCode: string, relay: RelayLocationRelayRedux) { + const relayLocation: RelayLocation = { hostname: [countryCode, cityCode, relay.hostname] }; + + const isSelected = this._isSelected(relayLocation); + + const onRef = isSelected + ? (element) => { + this._selectedCell = element; + } + : undefined; + + const handleSelect = !isSelected + ? () => { + this.props.onSelect(relayLocation); + } + : undefined; + return ( <Cell.CellButton - key={`${countryCode}_${relayCity.code}`} + key={`${countryCode}_${cityCode}_${relay.hostname}`} onPress={handleSelect} - disabled={!relayCity.hasActiveRelays} - cellHoverStyle={isSelected ? styles.sub_cell__selected : null} - style={isSelected ? styles.sub_cell__selected : styles.sub_cell} - testName="city" + cellHoverStyle={isSelected ? styles.sub_sub_cell__selected : null} + style={isSelected ? styles.sub_sub_cell__selected : styles.sub_sub_cell} + testName="relay" ref={onRef}> - {this._relayStatusIndicator(relayCity.hasActiveRelays, isSelected)} + {this._relayStatusIndicator(true, isSelected)} - <Cell.Label>{relayCity.name}</Cell.Label> + <Cell.Label>{relay.hostname}</Cell.Label> </Cell.CellButton> ); } diff --git a/gui/packages/desktop/src/renderer/components/SelectLocationStyles.js b/gui/packages/desktop/src/renderer/components/SelectLocationStyles.js index 49cbd441d5..d6d1abec65 100644 --- a/gui/packages/desktop/src/renderer/components/SelectLocationStyles.js +++ b/gui/packages/desktop/src/renderer/components/SelectLocationStyles.js @@ -60,6 +60,13 @@ export default { paddingLeft: 20, paddingRight: 0, }), + cell_selected: Styles.createViewStyle({ + paddingTop: 0, + paddingBottom: 0, + paddingLeft: 20, + paddingRight: 0, + backgroundColor: colors.green, + }), sub_cell: Styles.createViewStyle({ paddingTop: 0, paddingBottom: 0, @@ -74,11 +81,18 @@ export default { paddingLeft: 40, backgroundColor: colors.green, }), - cell_selected: Styles.createViewStyle({ + sub_sub_cell: Styles.createViewStyle({ + paddingTop: 0, + paddingBottom: 0, + paddingRight: 0, + paddingLeft: 60, + backgroundColor: colors.blue20, + }), + sub_sub_cell__selected: Styles.createViewStyle({ paddingTop: 0, paddingBottom: 0, - paddingLeft: 20, paddingRight: 0, + paddingLeft: 60, backgroundColor: colors.green, }), expand_chevron_hover: Styles.createViewStyle({ diff --git a/gui/packages/desktop/src/renderer/containers/ConnectPage.js b/gui/packages/desktop/src/renderer/containers/ConnectPage.js index a1390fab6f..828d92959e 100644 --- a/gui/packages/desktop/src/renderer/containers/ConnectPage.js +++ b/gui/packages/desktop/src/renderer/containers/ConnectPage.js @@ -36,6 +36,15 @@ function getRelayName( return city.name; } } + } else if (location.hostname) { + const [countryCode, cityCode, hostname] = location.hostname; + const country = relayLocations.find(({ code }) => code === countryCode); + if (country) { + const city = country.cities.find(({ code }) => code === cityCode); + if (city) { + return `${city.name} (${hostname})`; + } + } } return 'Unknown'; diff --git a/gui/packages/desktop/src/renderer/lib/daemon-rpc.js b/gui/packages/desktop/src/renderer/lib/daemon-rpc.js index e87f51930b..a712f9b73e 100644 --- a/gui/packages/desktop/src/renderer/lib/daemon-rpc.js +++ b/gui/packages/desktop/src/renderer/lib/daemon-rpc.js @@ -70,7 +70,10 @@ export type TunnelState = | BlockedState; export type RelayProtocol = 'tcp' | 'udp'; -export type RelayLocation = {| city: [string, string] |} | {| country: string |}; +export type RelayLocation = + | {| hostname: [string, string, string] |} + | {| city: [string, string] |} + | {| country: string |}; type OpenVpnConstraints = { port: 'any' | { only: number }, @@ -139,6 +142,9 @@ const RelaySettingsSchema = oneOf( location: constraint( oneOf( object({ + hostname: arrayOf(string), + }), + object({ city: arrayOf(string), }), object({ @@ -185,6 +191,15 @@ export type RelayListCity = { latitude: number, longitude: number, has_active_relays: boolean, + relays: Array<RelayListHostname>, +}; + +export type RelayListHostname = { + hostname: string, + ipv4_addr_in: string, + ipv4_addr_exit: string, + include_in_country: boolean, + weight: number, }; const RelayListSchema = object({ @@ -199,6 +214,15 @@ const RelayListSchema = object({ latitude: number, longitude: number, has_active_relays: boolean, + relays: arrayOf( + object({ + hostname: string, + ipv4_addr_in: string, + ipv4_addr_exit: string, + include_in_country: boolean, + weight: number, + }), + ), }), ), }), diff --git a/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js b/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js index 5f6c279e3f..5065a485f6 100644 --- a/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js +++ b/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js @@ -49,6 +49,10 @@ class NormalRelaySettingsBuilder { this._payload.location = { only: { city: [country, city] } }; return this; }, + hostname: (country: string, city: string, hostname: string) => { + this._payload.location = { only: { hostname: [country, city, hostname] } }; + return this; + }, any: () => { this._payload.location = 'any'; return this; @@ -58,6 +62,11 @@ class NormalRelaySettingsBuilder { return this.any(); } + if (location.hostname) { + const [country, city, hostname] = location.hostname; + return this.hostname(country, city, hostname); + } + if (location.city) { const [country, city] = location.city; return this.city(country, city); diff --git a/gui/packages/desktop/src/renderer/redux/settings/reducers.js b/gui/packages/desktop/src/renderer/redux/settings/reducers.js index 9870066edc..096dede5c2 100644 --- a/gui/packages/desktop/src/renderer/redux/settings/reducers.js +++ b/gui/packages/desktop/src/renderer/redux/settings/reducers.js @@ -19,12 +19,21 @@ export type RelaySettingsRedux = }, |}; +export type RelayLocationRelayRedux = { + hostname: string, + ipv4AddrIn: string, + ipv4AddrExit: string, + includeInCountry: boolean, + weight: number, +}; + export type RelayLocationCityRedux = { name: string, code: string, latitude: number, longitude: number, hasActiveRelays: boolean, + relays: Array<RelayLocationRelayRedux>, }; export type RelayLocationRedux = { diff --git a/gui/packages/desktop/test/components/SelectLocation.spec.js b/gui/packages/desktop/test/components/SelectLocation.spec.js index cd45ff2e76..3d35da0edd 100644 --- a/gui/packages/desktop/test/components/SelectLocation.spec.js +++ b/gui/packages/desktop/test/components/SelectLocation.spec.js @@ -29,6 +29,15 @@ describe('components/SelectLocation', () => { latitude: 0, longitude: 0, hasActiveRelays: true, + relays: [ + { + hostname: 'fake1.mullvad.net', + ipv4AddrIn: '192.168.0.100', + ipv4AddrExit: '192.168.1.100', + includeInCountry: true, + weight: 1, + }, + ], }, { name: 'Stockholm', @@ -36,6 +45,15 @@ describe('components/SelectLocation', () => { latitude: 0, longitude: 0, hasActiveRelays: true, + relays: [ + { + hostname: 'fake2.mullvad.net', + ipv4AddrIn: '192.168.0.101', + ipv4AddrExit: '192.168.1.101', + includeInCountry: true, + weight: 1, + }, + ], }, ], }, |
