summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2019-09-17 14:26:37 +0200
committerAndrej Mihajlov <and@mullvad.net>2019-09-18 16:11:57 +0200
commit7e46c618e39339544500db6f9fcf0920988ba03b (patch)
tree5f891d6a3d244f36d7dbc1811229545cbd3b627a /gui/src
parent0fbbfb9201ab841992c3cb021ed6a808db4ef6ef (diff)
downloadmullvadvpn-7e46c618e39339544500db6f9fcf0920988ba03b.tar.xz
mullvadvpn-7e46c618e39339544500db6f9fcf0920988ba03b.zip
Refactor LocationList
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/BridgeLocations.tsx57
-rw-r--r--gui/src/renderer/components/ExitLocations.tsx38
-rw-r--r--gui/src/renderer/components/LocationList.tsx309
-rw-r--r--gui/src/renderer/components/SelectLocation.tsx154
4 files changed, 437 insertions, 121 deletions
diff --git a/gui/src/renderer/components/BridgeLocations.tsx b/gui/src/renderer/components/BridgeLocations.tsx
new file mode 100644
index 0000000000..7d464e0926
--- /dev/null
+++ b/gui/src/renderer/components/BridgeLocations.tsx
@@ -0,0 +1,57 @@
+import * as React from 'react';
+import { LiftedConstraint, RelayLocation } from '../../shared/daemon-rpc-types';
+import { messages } from '../../shared/gettext';
+import { IRelayLocationRedux } from '../redux/settings/reducers';
+import LocationList, {
+ LocationSelection,
+ LocationSelectionType,
+ RelayLocations,
+ SpecialLocation,
+ SpecialLocationIcon,
+ SpecialLocations,
+} from './LocationList';
+
+export enum SpecialBridgeLocationType {
+ closestToExit = 0,
+}
+
+interface IBridgeLocationsProps {
+ source: IRelayLocationRedux[];
+ defaultExpandedLocations?: RelayLocation[];
+ selectedValue?: LiftedConstraint<RelayLocation>;
+ selectedElementRef?: React.Ref<React.ReactInstance>;
+ onSelect?: (value: LocationSelection<SpecialBridgeLocationType>) => void;
+}
+
+const BridgeLocations = React.forwardRef(function BridgeLocationsT(
+ props: IBridgeLocationsProps,
+ ref: React.Ref<LocationList<SpecialBridgeLocationType>>,
+) {
+ const selectedValue:
+ | LocationSelection<SpecialBridgeLocationType>
+ | undefined = props.selectedValue
+ ? props.selectedValue === 'any'
+ ? { type: LocationSelectionType.special, value: SpecialBridgeLocationType.closestToExit }
+ : { type: LocationSelectionType.relay, value: props.selectedValue }
+ : undefined;
+
+ return (
+ <LocationList
+ ref={ref}
+ defaultExpandedLocations={props.defaultExpandedLocations}
+ selectedValue={selectedValue}
+ selectedElementRef={props.selectedElementRef}
+ onSelect={props.onSelect}>
+ <SpecialLocations>
+ <SpecialLocation
+ icon={SpecialLocationIcon.geoLocation}
+ value={SpecialBridgeLocationType.closestToExit}>
+ {messages.pgettext('select-location-view', 'Closest to exit server')}
+ </SpecialLocation>
+ </SpecialLocations>
+ <RelayLocations source={props.source} />
+ </LocationList>
+ );
+});
+
+export default BridgeLocations;
diff --git a/gui/src/renderer/components/ExitLocations.tsx b/gui/src/renderer/components/ExitLocations.tsx
new file mode 100644
index 0000000000..9fcc1e8ad4
--- /dev/null
+++ b/gui/src/renderer/components/ExitLocations.tsx
@@ -0,0 +1,38 @@
+import * as React from 'react';
+import { RelayLocation } from '../../shared/daemon-rpc-types';
+import { IRelayLocationRedux } from '../redux/settings/reducers';
+import LocationList, {
+ LocationSelection,
+ LocationSelectionType,
+ RelayLocations,
+} from './LocationList';
+
+interface IExitLocationsProps {
+ source: IRelayLocationRedux[];
+ defaultExpandedLocations?: RelayLocation[];
+ selectedValue?: RelayLocation;
+ selectedElementRef?: React.Ref<React.ReactInstance>;
+ onSelect?: (value: LocationSelection<never>) => void;
+}
+
+const ExitLocations = React.forwardRef(function ExitLocationsT(
+ props: IExitLocationsProps,
+ ref: React.Ref<LocationList<never>>,
+) {
+ const selectedValue: LocationSelection<never> | undefined = props.selectedValue
+ ? { type: LocationSelectionType.relay, value: props.selectedValue }
+ : undefined;
+
+ return (
+ <LocationList
+ ref={ref}
+ defaultExpandedLocations={props.defaultExpandedLocations}
+ selectedValue={selectedValue}
+ selectedElementRef={props.selectedElementRef}
+ onSelect={props.onSelect}>
+ <RelayLocations source={props.source} />
+ </LocationList>
+ );
+});
+
+export default ExitLocations;
diff --git a/gui/src/renderer/components/LocationList.tsx b/gui/src/renderer/components/LocationList.tsx
index 72a3b64f75..99adcd65b4 100644
--- a/gui/src/renderer/components/LocationList.tsx
+++ b/gui/src/renderer/components/LocationList.tsx
@@ -1,5 +1,6 @@
import * as React from 'react';
-import { Component, View } from 'reactxp';
+import { Component, Styles, View } from 'reactxp';
+import { colors } from '../../config.json';
import {
compareRelayLocation,
compareRelayLocationLoose,
@@ -8,49 +9,266 @@ import {
} from '../../shared/daemon-rpc-types';
import { countries, relayLocations } from '../../shared/gettext';
import { IRelayLocationRedux } from '../redux/settings/reducers';
+import * as Cell from './Cell';
import CityRow from './CityRow';
import CountryRow from './CountryRow';
import RelayRow from './RelayRow';
-interface IProps {
- relayLocations: IRelayLocationRedux[];
- selectedLocation?: RelayLocation;
- onSelect: (location: RelayLocation) => void;
+const styles = {
+ selectedCell: Styles.createViewStyle({
+ backgroundColor: colors.green,
+ }),
+};
+
+export enum LocationSelectionType {
+ relay = 'relay',
+ special = 'special',
}
-interface IState {
- selectedLocation?: RelayLocation;
- expandedItems: RelayLocation[];
+export type LocationSelection<SpecialValueType> =
+ | { type: LocationSelectionType.special; value: SpecialValueType }
+ | { type: LocationSelectionType.relay; value: RelayLocation };
+
+interface ILocationListState<SpecialValueType> {
+ selectedValue?: LocationSelection<SpecialValueType>;
+ expandedLocations: RelayLocation[];
}
-interface ICommonCellProps<T> {
- location: RelayLocation;
- selected: boolean;
- ref?: React.RefObject<T>;
+interface ILocationListProps<SpecialValueType> {
+ defaultExpandedLocations?: RelayLocation[];
+ selectedValue?: LocationSelection<SpecialValueType>;
+ selectedElementRef?: React.Ref<React.ReactInstance>;
+ onSelect?: (value: LocationSelection<SpecialValueType>) => void;
}
-export default class LocationList extends Component<IProps, IState> {
- public selectedCell = React.createRef<React.ReactNode>();
+export default class LocationList<SpecialValueType> extends Component<
+ ILocationListProps<SpecialValueType>,
+ ILocationListState<SpecialValueType>
+> {
+ public state: ILocationListState<SpecialValueType> = {
+ expandedLocations: [],
+ };
- constructor(props: IProps) {
+ public selectedRelayLocationRef: React.ReactInstance | null = null;
+ public selectedSpecialLocationRef: React.ReactInstance | null = null;
+
+ constructor(props: ILocationListProps<SpecialValueType>) {
super(props);
- this.state = {
- expandedItems: props.selectedLocation ? expandRelayLocation(props.selectedLocation) : [],
- selectedLocation: props.selectedLocation,
- };
+ if (props.selectedValue) {
+ const expandedLocations =
+ props.defaultExpandedLocations ||
+ (props.selectedValue.type === LocationSelectionType.relay
+ ? expandRelayLocation(props.selectedValue.value)
+ : []);
+
+ this.state = {
+ selectedValue: props.selectedValue,
+ expandedLocations,
+ };
+ }
+ }
+
+ public getExpandedLocations(): RelayLocation[] {
+ return this.state.expandedLocations;
}
- public componentDidUpdate(prevProps: IProps, _prevState: IState) {
- if (this.props.selectedLocation !== prevProps.selectedLocation) {
- this.setState({ selectedLocation: this.props.selectedLocation });
+ public componentDidUpdate(prevProps: ILocationListProps<SpecialValueType>) {
+ if (!compareLocationSelectionLoose(prevProps.selectedValue, this.props.selectedValue)) {
+ this.setState({ selectedValue: this.props.selectedValue });
}
}
public render() {
+ const selection = this.state.selectedValue;
+ const specialSelection =
+ selection && selection.type === LocationSelectionType.special ? selection.value : undefined;
+ const relaySelection =
+ selection && selection.type === LocationSelectionType.relay ? selection.value : undefined;
+
+ return (
+ <View>
+ {React.Children.map(this.props.children, (child) => {
+ if (React.isValidElement(child)) {
+ if (child.type === SpecialLocations) {
+ return React.cloneElement(child, {
+ ...child.props,
+ selectedElementRef: this.onSpecialLocationRef,
+ selectedValue: specialSelection,
+ onSelect: this.onSelectSpecialLocation,
+ });
+ } else if (child.type === RelayLocations) {
+ return React.cloneElement(child, {
+ ...child.props,
+ selectedLocation: relaySelection,
+ selectedElementRef: this.onRelayLocationRef,
+ expandedItems: this.state.expandedLocations,
+ onSelect: this.onSelectRelayLocation,
+ onExpand: this.onExpandRelayLocation,
+ });
+ }
+ }
+ return child;
+ })}
+ </View>
+ );
+ }
+
+ private onSpecialLocationRef = (ref: React.ReactInstance | null) => {
+ this.selectedSpecialLocationRef = ref;
+
+ this.updateExternalRef();
+ };
+
+ private onRelayLocationRef = (ref: React.ReactInstance | null) => {
+ this.selectedRelayLocationRef = ref;
+
+ this.updateExternalRef();
+ };
+
+ private updateExternalRef() {
+ if (this.props.selectedElementRef) {
+ const value = this.selectedRelayLocationRef || this.selectedSpecialLocationRef;
+
+ if (typeof this.props.selectedElementRef === 'function') {
+ this.props.selectedElementRef(value);
+ } else {
+ // @ts-ignore
+ this.props.selectedElementRef.current = value;
+ }
+ }
+ }
+
+ private onSelectRelayLocation = (value: RelayLocation) => {
+ const selectedValue: LocationSelection<SpecialValueType> = {
+ type: LocationSelectionType.relay,
+ value,
+ };
+
+ this.setState({ selectedValue }, () => {
+ this.notifySelection(selectedValue);
+ });
+ };
+
+ private onSelectSpecialLocation = (value: SpecialValueType) => {
+ const selectedValue: LocationSelection<SpecialValueType> = {
+ type: LocationSelectionType.special,
+ value,
+ };
+
+ this.setState({ selectedValue }, () => {
+ this.notifySelection(selectedValue);
+ });
+ };
+
+ private notifySelection(value: LocationSelection<SpecialValueType>) {
+ if (this.props.onSelect) {
+ this.props.onSelect(value);
+ }
+ }
+
+ private onExpandRelayLocation = (location: RelayLocation, expand: boolean) => {
+ this.setState((state) => {
+ const expandedLocations = state.expandedLocations.filter(
+ (item) => !compareRelayLocation(item, location),
+ );
+
+ if (expand) {
+ expandedLocations.push(location);
+ }
+
+ return {
+ ...state,
+ expandedLocations,
+ };
+ });
+ };
+}
+
+export enum SpecialLocationIcon {
+ geoLocation = 'icon-nearest',
+}
+
+interface ISpecialLocationsProps<T> {
+ children: React.ReactNode;
+ selectedValue?: T;
+ selectedElementRef?: React.Ref<SpecialLocation<T>>;
+ onSelect?: (value: T) => void;
+}
+
+export function SpecialLocations<T>(props: ISpecialLocationsProps<T>) {
+ return (
+ <View>
+ {React.Children.map(props.children, (child) => {
+ if (React.isValidElement(child) && child.type === SpecialLocation) {
+ const isSelected = props.selectedValue === child.props.value;
+
+ return React.cloneElement(child, {
+ ...child.props,
+ ref: isSelected ? props.selectedElementRef : undefined,
+ onSelect: props.onSelect,
+ isSelected,
+ });
+ } else {
+ return undefined;
+ }
+ })}
+ </View>
+ );
+}
+
+interface ISpecialLocationProps<T> {
+ icon: SpecialLocationIcon;
+ value: T;
+ isSelected?: boolean;
+ onSelect?: (value: T) => void;
+}
+
+export class SpecialLocation<T> extends Component<ISpecialLocationProps<T>> {
+ public render() {
+ return (
+ <Cell.CellButton
+ style={this.props.isSelected ? styles.selectedCell : undefined}
+ cellHoverStyle={this.props.isSelected ? styles.selectedCell : undefined}
+ onPress={this.onSelect}>
+ <Cell.Icon
+ source={this.props.isSelected ? 'icon-tick' : this.props.icon}
+ tintColor={colors.white}
+ height={24}
+ width={24}
+ />
+ <Cell.Label>{this.props.children}</Cell.Label>
+ </Cell.CellButton>
+ );
+ }
+
+ private onSelect = () => {
+ if (!this.props.isSelected && this.props.onSelect) {
+ this.props.onSelect(this.props.value);
+ }
+ };
+}
+
+interface IRelayLocationsProps {
+ source: IRelayLocationRedux[];
+ selectedLocation?: RelayLocation;
+ selectedElementRef?: React.Ref<React.ReactInstance>;
+ expandedItems?: RelayLocation[];
+ onSelect?: (location: RelayLocation) => void;
+ onExpand?: (location: RelayLocation, expand: boolean) => void;
+}
+
+interface ICommonCellProps {
+ location: RelayLocation;
+ selected: boolean;
+ ref?: React.Ref<any>;
+}
+
+export class RelayLocations extends Component<IRelayLocationsProps> {
+ public render() {
return (
<View>
- {this.props.relayLocations.map((relayCountry) => {
+ {this.props.source.map((relayCountry) => {
const countryLocation: RelayLocation = { country: relayCountry.code };
return (
@@ -102,43 +320,33 @@ export default class LocationList extends Component<IProps, IState> {
}
private isExpanded(relayLocation: RelayLocation) {
- return this.state.expandedItems.some((location) =>
+ return (this.props.expandedItems || []).some((location) =>
compareRelayLocation(location, relayLocation),
);
}
private isSelected(relayLocation: RelayLocation) {
- return compareRelayLocationLoose(this.state.selectedLocation, relayLocation);
+ return compareRelayLocationLoose(this.props.selectedLocation, relayLocation);
}
private handleSelection = (location: RelayLocation) => {
- if (!compareRelayLocationLoose(this.state.selectedLocation, location)) {
- this.setState({ selectedLocation: location }, () => {
+ if (!compareRelayLocationLoose(this.props.selectedLocation, location)) {
+ if (this.props.onSelect) {
this.props.onSelect(location);
- });
+ }
}
};
private handleExpand = (location: RelayLocation, expand: boolean) => {
- this.setState((state) => {
- const expandedItems = state.expandedItems.filter(
- (item) => !compareRelayLocation(item, location),
- );
-
- if (expand) {
- expandedItems.push(location);
- }
-
- return {
- ...state,
- expandedItems,
- };
- });
+ if (this.props.onExpand) {
+ this.props.onExpand(location, expand);
+ }
};
- private getCommonCellProps<T>(location: RelayLocation): ICommonCellProps<T> {
+ private getCommonCellProps(location: RelayLocation): ICommonCellProps {
const selected = this.isSelected(location);
- const ref = selected ? (this.selectedCell as React.RefObject<T>) : undefined;
+ const ref =
+ selected && this.props.selectedElementRef ? this.props.selectedElementRef : undefined;
return { ref, selected, location };
}
@@ -160,3 +368,16 @@ function expandRelayLocation(location: RelayLocation): RelayLocation[] {
function getLocationKey(location: RelayLocation): string {
return relayLocationComponents(location).join('-');
}
+
+function compareLocationSelectionLoose<SpecialValueType>(
+ lhs?: LocationSelection<SpecialValueType>,
+ rhs?: LocationSelection<SpecialValueType>,
+) {
+ if (!lhs || !rhs) {
+ return lhs === rhs;
+ } else if (lhs.type === LocationSelectionType.relay && rhs.type === LocationSelectionType.relay) {
+ return compareRelayLocation(lhs.value, rhs.value);
+ } else {
+ return lhs.value === rhs.value;
+ }
+}
diff --git a/gui/src/renderer/components/SelectLocation.tsx b/gui/src/renderer/components/SelectLocation.tsx
index 0018446291..68ca5db8db 100644
--- a/gui/src/renderer/components/SelectLocation.tsx
+++ b/gui/src/renderer/components/SelectLocation.tsx
@@ -1,15 +1,15 @@
import * as React from 'react';
import ReactDOM from 'react-dom';
import { Component, View } from 'reactxp';
-import { colors } from '../../config.json';
import { LiftedConstraint, RelayLocation } from '../../shared/daemon-rpc-types';
import { messages } from '../../shared/gettext';
import { IRelayLocationRedux } from '../redux/settings/reducers';
import { LocationScope } from '../redux/userinterface/reducers';
-import * as Cell from './Cell';
+import BridgeLocations, { SpecialBridgeLocationType } from './BridgeLocations';
import CustomScrollbars from './CustomScrollbars';
+import ExitLocations from './ExitLocations';
import { Container, Layout } from './Layout';
-import LocationList from './LocationList';
+import LocationList, { LocationSelection, LocationSelectionType } from './LocationList';
import {
CloseBarItem,
NavigationBar,
@@ -37,32 +37,47 @@ interface IProps {
onSelectClosestToExit: () => void;
}
+interface ISelectLocationSnapshot {
+ scrollPosition: [number, number];
+ expandedLocations: RelayLocation[];
+}
+
export default class SelectLocation extends Component<IProps> {
private scrollView = React.createRef<CustomScrollbars>();
- private exitLocationList = React.createRef<LocationList>();
- private bridgeLocationList = React.createRef<LocationList>();
+ private selectedExitLocationRef = React.createRef<React.ReactInstance>();
+ private selectedBridgeLocationRef = React.createRef<React.ReactInstance>();
+
+ private exitLocationList = React.createRef<LocationList<never>>();
+ private bridgeLocationList = React.createRef<LocationList<SpecialBridgeLocationType>>();
- private scrollPositionByScope: { [index: number]: [number, number] } = {};
+ private snapshotByScope: { [index: number]: ISelectLocationSnapshot } = {};
public componentDidMount() {
this.scrollToSelectedCell();
}
- public componentDidUpdate(prevProps: IProps, _prevState: {}, snapshot?: [number, number]) {
+ public componentDidUpdate(prevProps: IProps, _prevState: {}, snapshot?: ISelectLocationSnapshot) {
if (this.props.locationScope !== prevProps.locationScope) {
this.restoreScrollPosition(this.props.locationScope);
if (snapshot) {
- this.saveScrollPosition(prevProps.locationScope, snapshot);
+ this.snapshotByScope[prevProps.locationScope] = snapshot;
}
}
}
- public getSnapshotBeforeUpdate(_prevProps: IProps) {
+ public getSnapshotBeforeUpdate(prevProps: IProps): ISelectLocationSnapshot | undefined {
const scrollView = this.scrollView.current;
+ const locationList =
+ prevProps.locationScope === LocationScope.relay
+ ? this.exitLocationList.current
+ : this.bridgeLocationList.current;
- if (scrollView) {
- return scrollView.getScrollPosition();
+ if (scrollView && locationList) {
+ return {
+ scrollPosition: scrollView.getScrollPosition(),
+ expandedLocations: locationList.getExpandedLocations(),
+ };
} else {
return undefined;
}
@@ -113,33 +128,23 @@ export default class SelectLocation extends Component<IProps> {
<NavigationScrollbars ref={this.scrollView}>
<View style={styles.content}>
{this.props.locationScope === LocationScope.relay ? (
- <LocationList
- key={'exit-locations'}
+ <ExitLocations
ref={this.exitLocationList}
- selectedLocation={this.props.selectedExitLocation}
- relayLocations={this.props.relayLocations}
- onSelect={this.props.onSelectExitLocation}
+ source={this.props.relayLocations}
+ defaultExpandedLocations={this.getExpandedLocationsFromSnapshot()}
+ selectedValue={this.props.selectedExitLocation}
+ selectedElementRef={this.selectedExitLocationRef}
+ onSelect={this.onSelectExitLocation}
/>
) : (
- <React.Fragment>
- <View>
- <ClosestToExitCell
- onSelect={this.props.onSelectClosestToExit}
- isSelected={this.props.selectedBridgeLocation === 'any'}
- />
- </View>
- <LocationList
- key={'bridge-locations'}
- ref={this.bridgeLocationList}
- selectedLocation={
- this.props.selectedBridgeLocation !== 'any'
- ? this.props.selectedBridgeLocation
- : undefined
- }
- relayLocations={this.props.bridgeLocations}
- onSelect={this.props.onSelectBridgeLocation}
- />
- </React.Fragment>
+ <BridgeLocations
+ ref={this.bridgeLocationList}
+ source={this.props.bridgeLocations}
+ defaultExpandedLocations={this.getExpandedLocationsFromSnapshot()}
+ selectedValue={this.props.selectedBridgeLocation}
+ selectedElementRef={this.selectedBridgeLocationRef}
+ onSelect={this.onSelectBridgeLocation}
+ />
)}
</View>
</NavigationScrollbars>
@@ -151,20 +156,25 @@ export default class SelectLocation extends Component<IProps> {
);
}
- public saveScrollPosition(scope: LocationScope, position: [number, number]) {
- this.scrollPositionByScope[scope] = position;
- }
-
public restoreScrollPosition(scope: LocationScope) {
- const prevScrollPos = this.scrollPositionByScope[scope];
+ const snapshot = this.snapshotByScope[scope];
- if (prevScrollPos) {
- this.scrollToPosition(...prevScrollPos);
+ if (snapshot) {
+ this.scrollToPosition(...snapshot.scrollPosition);
} else {
this.scrollToSelectedCell();
}
}
+ private getExpandedLocationsFromSnapshot(): RelayLocation[] | undefined {
+ const snapshot = this.snapshotByScope[this.props.locationScope];
+ if (snapshot) {
+ return snapshot.expandedLocations;
+ } else {
+ return undefined;
+ }
+ }
+
private scrollToPosition(x: number, y: number) {
const scrollView = this.scrollView.current;
if (scrollView) {
@@ -175,46 +185,36 @@ export default class SelectLocation extends Component<IProps> {
private scrollToSelectedCell() {
const ref =
this.props.locationScope === LocationScope.relay
- ? this.exitLocationList
- : this.bridgeLocationList;
- const locationList = ref.current;
-
- if (locationList) {
- const cell = locationList.selectedCell.current;
- const scrollView = this.scrollView.current;
+ ? this.selectedExitLocationRef.current
+ : this.selectedBridgeLocationRef.current;
+ const scrollView = this.scrollView.current;
- if (scrollView) {
- if (cell) {
- const cellDOMNode = ReactDOM.findDOMNode(cell as Element);
- if (cellDOMNode instanceof HTMLElement) {
- scrollView.scrollToElement(cellDOMNode, 'middle');
- }
- } else {
- scrollView.scrollToTop();
+ if (scrollView) {
+ if (ref) {
+ const cellDOMNode = ReactDOM.findDOMNode(ref);
+ if (cellDOMNode instanceof HTMLElement) {
+ scrollView.scrollToElement(cellDOMNode, 'middle');
}
+ } else {
+ scrollView.scrollToTop();
}
}
}
-}
-interface IClosestToExitCellProps {
- isSelected: boolean;
- onSelect: () => void;
-}
+ private onSelectExitLocation = (location: LocationSelection<never>) => {
+ if (location.type === LocationSelectionType.relay) {
+ this.props.onSelectExitLocation(location.value);
+ }
+ };
-function ClosestToExitCell(props: IClosestToExitCellProps) {
- return (
- <Cell.CellButton
- style={props.isSelected ? styles.selectedCell : undefined}
- cellHoverStyle={props.isSelected ? styles.selectedCell : undefined}
- onPress={props.onSelect}>
- <Cell.Icon
- source={props.isSelected ? 'icon-tick' : 'icon-nearest'}
- tintColor={colors.white}
- height={24}
- width={24}
- />
- <Cell.Label>{messages.pgettext('select-location-view', 'Closest to exit server')}</Cell.Label>
- </Cell.CellButton>
- );
+ private onSelectBridgeLocation = (location: LocationSelection<SpecialBridgeLocationType>) => {
+ if (location.type === LocationSelectionType.relay) {
+ this.props.onSelectBridgeLocation(location.value);
+ } else if (
+ location.type === LocationSelectionType.special &&
+ location.value === SpecialBridgeLocationType.closestToExit
+ ) {
+ this.props.onSelectClosestToExit();
+ }
+ };
}