diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2021-08-17 07:57:39 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2021-08-17 09:41:03 +0200 |
| commit | ccafdf50c837fd1de68ecb26364cb4100ada593e (patch) | |
| tree | 6311696843a86f9cd619480537ebbf4a74a7c0dc | |
| parent | 8fd9f53a8c909bd757cc777d4e8d66fe6097a4a8 (diff) | |
| download | mullvadvpn-ccafdf50c837fd1de68ecb26364cb4100ada593e.tar.xz mullvadvpn-ccafdf50c837fd1de68ecb26364cb4100ada593e.zip | |
Add filter box to select location view
| -rw-r--r-- | gui/src/renderer/components/SelectLocation.tsx | 61 | ||||
| -rw-r--r-- | gui/src/renderer/components/SelectLocationStyles.tsx | 33 | ||||
| -rw-r--r-- | gui/src/renderer/components/SettingsHeader.tsx | 3 | ||||
| -rw-r--r-- | gui/src/renderer/containers/SelectLocationPage.tsx | 43 |
4 files changed, 101 insertions, 39 deletions
diff --git a/gui/src/renderer/components/SelectLocation.tsx b/gui/src/renderer/components/SelectLocation.tsx index b3226821a0..3ac5147ea0 100644 --- a/gui/src/renderer/components/SelectLocation.tsx +++ b/gui/src/renderer/components/SelectLocation.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { sprintf } from 'sprintf-js'; import { colors } from '../../config.json'; import { LiftedConstraint, RelayLocation } from '../../shared/daemon-rpc-types'; import { messages } from '../../shared/gettext'; @@ -28,8 +29,12 @@ import { StyledNavigationBarAttachment, StyledScopeBar, StyledFilterByProviderButton, + StyledProvidersCount, + StyledProviderCountRow, + StyledClearProvidersButton, + StyledSettingsHeader, } from './SelectLocationStyles'; -import { HeaderSubTitle } from './SettingsHeader'; +import { HeaderTitle } from './SettingsHeader'; interface IProps { locationScope: LocationScope; @@ -38,12 +43,14 @@ interface IProps { relayLocations: IRelayLocationRedux[]; bridgeLocations: IRelayLocationRedux[]; allowBridgeSelection: boolean; + providers: string[]; onClose: () => void; onViewFilterByProvider: () => void; onChangeLocationScope: (location: LocationScope) => void; onSelectExitLocation: (location: RelayLocation) => void; onSelectBridgeLocation: (location: RelayLocation) => void; onSelectClosestToExit: () => void; + onClearProviders: () => void; } interface IState { @@ -113,12 +120,7 @@ export default class SelectLocation extends React.Component<IProps, IState> { <NavigationBar alwaysDisplayBarTitle={true}> <NavigationItems> <CloseBarItem action={this.props.onClose} /> - <TitleBarItem> - { - // TRANSLATORS: Title label in navigation bar - messages.pgettext('select-location-nav', 'Select location') - } - </TitleBarItem> + <TitleBarItem /> <StyledFilterContainer ref={this.filterButtonRef}> <StyledFilterIconButton onClick={this.toggleFilterMenu}> @@ -140,17 +142,42 @@ export default class SelectLocation extends React.Component<IProps, IState> { </StyledFilterContainer> </NavigationItems> <StyledNavigationBarAttachment> - <HeaderSubTitle> - {this.props.allowBridgeSelection - ? messages.pgettext( - 'select-location-view', - 'While connected, your traffic will be routed through two secure locations, the entry point (a bridge server) and the exit point (a VPN server).', - ) - : messages.pgettext( - 'select-location-view', - 'While connected, your real location is masked with a private and secure location in the selected region.', + <StyledSettingsHeader> + <HeaderTitle> + { + // TRANSLATORS: Heading in select location view + messages.pgettext('select-location-nav', 'Select location') + } + </HeaderTitle> + </StyledSettingsHeader> + + {this.props.providers.length > 0 && ( + <StyledProviderCountRow> + {messages.pgettext('select-location-view', 'Filtered:')} + <StyledProvidersCount> + {sprintf( + messages.pgettext( + 'select-location-view', + 'Providers: %(numberOfProviders)d', + ), + { + numberOfProviders: this.props.providers.length, + }, )} - </HeaderSubTitle> + <StyledClearProvidersButton + aria-label={messages.gettext('Clear')} + onClick={this.props.onClearProviders}> + <ImageView + height={16} + width={16} + source="icon-close" + tintColor={colors.white60} + tintHoverColor={colors.white80} + /> + </StyledClearProvidersButton> + </StyledProvidersCount> + </StyledProviderCountRow> + )} {this.props.allowBridgeSelection && ( <StyledScopeBar defaultSelectedIndex={this.props.locationScope} diff --git a/gui/src/renderer/components/SelectLocationStyles.tsx b/gui/src/renderer/components/SelectLocationStyles.tsx index 2c317ddddc..bf0dc038e8 100644 --- a/gui/src/renderer/components/SelectLocationStyles.tsx +++ b/gui/src/renderer/components/SelectLocationStyles.tsx @@ -3,6 +3,7 @@ import { colors } from '../../config.json'; import { smallText } from './common-styles'; import { Container } from './Layout'; import { ScopeBar } from './ScopeBar'; +import SettingsHeader from './SettingsHeader'; export const StyledContainer = styled(Container)({ backgroundColor: colors.darkBlue, @@ -59,3 +60,35 @@ export const StyledFilterByProviderButton = styled.button({ backgroundColor: colors.blue80, }, }); + +export const StyledSettingsHeader = styled(SettingsHeader)({ + paddingLeft: '6px', + paddingBottom: '11px', +}); + +export const StyledProviderCountRow = styled.div({ + ...smallText, + color: colors.white, + marginLeft: '6px', + marginBottom: '8px', +}); + +export const StyledProvidersCount = styled.div({ + ...smallText, + display: 'inline-flex', + alignItems: 'center', + backgroundColor: colors.blue, + borderRadius: '4px', + padding: '3px 8px', + marginLeft: '6px', + color: colors.white, +}); + +export const StyledClearProvidersButton = styled.div({ + display: 'inline-block', + borderWidth: 0, + padding: 0, + margin: '0 0 0 6px', + cursor: 'default', + backgroundColor: 'transparent', +}); diff --git a/gui/src/renderer/components/SettingsHeader.tsx b/gui/src/renderer/components/SettingsHeader.tsx index 808b6f80c7..ea1d2ab634 100644 --- a/gui/src/renderer/components/SettingsHeader.tsx +++ b/gui/src/renderer/components/SettingsHeader.tsx @@ -18,11 +18,12 @@ export const HeaderSubTitle = styled.span(smallText); interface ISettingsHeaderProps { children?: React.ReactNode; + className?: string; } export default function SettingsHeader(props: ISettingsHeaderProps) { return ( - <Container> + <Container className={props.className}> {React.Children.map(props.children, (child) => { return React.isValidElement(child) ? <ContentWrapper>{child}</ContentWrapper> : undefined; })} diff --git a/gui/src/renderer/containers/SelectLocationPage.tsx b/gui/src/renderer/containers/SelectLocationPage.tsx index edfac6c415..e930dec0fe 100644 --- a/gui/src/renderer/containers/SelectLocationPage.tsx +++ b/gui/src/renderer/containers/SelectLocationPage.tsx @@ -8,7 +8,7 @@ import SelectLocation from '../components/SelectLocation'; import withAppContext, { IAppContext } from '../context'; import { IHistoryProps, withHistory } from '../lib/history'; import { RoutePath } from '../lib/routes'; -import { IRelayLocationRedux, RelaySettingsRedux } from '../redux/settings/reducers'; +import { IRelayLocationRedux } from '../redux/settings/reducers'; import { IReduxState, ReduxDispatch } from '../redux/store'; import userInterfaceActions from '../redux/userinterface/actions'; import { LocationScope } from '../redux/userinterface/reducers'; @@ -33,16 +33,17 @@ const mapStateToProps = (state: IReduxState) => { ? state.userInterface.locationScope : LocationScope.relay; + const relaySettings = state.settings.relaySettings; + const providers = 'normal' in relaySettings ? relaySettings.normal.providers : []; + return { selectedExitLocation, selectedBridgeLocation, - relayLocations: filterLocationsByProvider( - state.settings.relayLocations, - state.settings.relaySettings, - ), + relayLocations: filterLocationsByProvider(state.settings.relayLocations, providers), bridgeLocations: state.settings.bridgeLocations, locationScope, allowBridgeSelection, + providers, }; }; const mapDispatchToProps = (dispatch: ReduxDispatch, props: IHistoryProps & IAppContext) => { @@ -89,29 +90,29 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IHistoryProps & IApp log.error(`Failed to set the bridge location to closest to exit: ${e.message}`); } }, + onClearProviders: async () => { + await props.app.updateRelaySettings({ normal: { providers: [] } }); + }, }; }; function filterLocationsByProvider( locations: IRelayLocationRedux[], - relaySettings: RelaySettingsRedux, + providers: string[], ): IRelayLocationRedux[] { - const providers = - 'normal' in relaySettings && relaySettings.normal.providers.length > 0 - ? relaySettings.normal.providers - : undefined; - - return locations - .map((country) => ({ - ...country, - cities: country.cities - .map((city) => ({ - ...city, - relays: city.relays.filter((relay) => providers?.includes(relay.provider) ?? true), + return providers.length === 0 + ? locations + : locations + .map((country) => ({ + ...country, + cities: country.cities + .map((city) => ({ + ...city, + relays: city.relays.filter((relay) => providers.includes(relay.provider)), + })) + .filter((city) => city.relays.length > 0), })) - .filter((city) => city.relays.length > 0), - })) - .filter((country) => country.cities.length > 0); + .filter((country) => country.cities.length > 0); } export default withAppContext( |
