diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2022-07-19 10:55:29 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2022-07-20 13:48:15 +0200 |
| commit | 057bfc81ab6cc95cfec6fc7d9049bf87bb405097 (patch) | |
| tree | 6480de1a6f5a5d115ac64188cfd546cbcf94f486 /gui/src/renderer/components | |
| parent | 163f3370d7655d099636a58869d655547b0400f5 (diff) | |
| download | mullvadvpn-057bfc81ab6cc95cfec6fc7d9049bf87bb405097.tar.xz mullvadvpn-057bfc81ab6cc95cfec6fc7d9049bf87bb405097.zip | |
Make filter update when others are changed
Diffstat (limited to 'gui/src/renderer/components')
| -rw-r--r-- | gui/src/renderer/components/Filter.tsx | 125 |
1 files changed, 91 insertions, 34 deletions
diff --git a/gui/src/renderer/components/Filter.tsx b/gui/src/renderer/components/Filter.tsx index a7b8272831..38d27d6ec6 100644 --- a/gui/src/renderer/components/Filter.tsx +++ b/gui/src/renderer/components/Filter.tsx @@ -5,8 +5,10 @@ import { colors } from '../../config.json'; import { Ownership } from '../../shared/daemon-rpc-types'; import { messages } from '../../shared/gettext'; import { useAppContext } from '../context'; +import filterLocations from '../lib/filter-locations'; import { useHistory } from '../lib/history'; import { useBoolean } from '../lib/utilityHooks'; +import { IRelayLocationRedux } from '../redux/settings/reducers'; import { IReduxState, useSelector } from '../redux/store'; import Accordion from './Accordion'; import * as AppButton from './AppButton'; @@ -47,6 +49,15 @@ export default function Filter() { const initialProviders = useSelector(providersSelector); const [providers, setProviders] = useState<Record<string, boolean>>(initialProviders); + const formattedProviderList = useMemo(() => { + // If all providers are selected it's represented as an empty array. + return Object.values(providers).every((provider) => provider) + ? [] + : Object.entries(providers) + .filter(([, selected]) => selected) + .map(([name]) => name); + }, [providers]); + const initialOwnership = useSelector((state) => 'normal' in state.settings.relaySettings ? state.settings.relaySettings.normal.ownership @@ -54,17 +65,15 @@ export default function Filter() { ); const [ownership, setOwnership] = useState<Ownership>(initialOwnership); - const onApply = useCallback(async () => { - // If all providers are selected it's represented as an empty array. - const selectedProviders = Object.values(providers).every((provider) => provider) - ? [] - : Object.entries(providers) - .filter(([, selected]) => selected) - .map(([name]) => name); + const { availableProviders, availableOwnershipOptions } = useFilteredFilters( + formattedProviderList, + ownership, + ); - await updateRelaySettings({ normal: { providers: selectedProviders, ownership } }); + const onApply = useCallback(async () => { + await updateRelaySettings({ normal: { providers: formattedProviderList, ownership } }); history.pop(); - }, [providers, ownership, history, updateRelaySettings]); + }, [formattedProviderList, ownership, history, updateRelaySettings]); return ( <BackAction action={history.pop}> @@ -82,8 +91,16 @@ export default function Filter() { </NavigationItems> </NavigationBar> <StyledNavigationScrollbars> - <FilterByOwnership ownership={ownership} setOwnership={setOwnership} /> - <FilterByProvider providers={providers} setProviders={setProviders} /> + <FilterByOwnership + ownership={ownership} + availableOptions={availableOwnershipOptions} + setOwnership={setOwnership} + /> + <FilterByProvider + providers={providers} + availableOptions={availableProviders} + setProviders={setProviders} + /> </StyledNavigationScrollbars> <StyledFooter> <AppButton.GreenButton @@ -99,6 +116,44 @@ export default function Filter() { ); } +function useFilteredFilters(providers: string[], ownership: Ownership) { + const locations = useSelector((state) => + state.settings.relayLocations.concat( + state.settings.bridgeState === 'on' ? state.settings.bridgeLocations : [], + ), + ); + + const availableProviders = useMemo(() => { + const filteredRelays = filterLocations(locations, [], ownership); + return providersFromRelays(filteredRelays); + }, [locations, ownership]); + const availableOwnershipOptions = useMemo(() => { + const filteredRelays = filterLocations(locations, providers, Ownership.any); + const filteredRelayOwnership = filteredRelays.flatMap((country) => + country.cities.flatMap((city) => city.relays.map((relay) => relay.owned)), + ); + + const ownershipOptions = [Ownership.any]; + if (filteredRelayOwnership.includes(true)) { + ownershipOptions.push(Ownership.mullvadOwned); + } + if (filteredRelayOwnership.includes(false)) { + ownershipOptions.push(Ownership.rented); + } + + return ownershipOptions; + }, [locations, providers]); + + return { availableProviders, availableOwnershipOptions }; +} + +function providersFromRelays(relays: IRelayLocationRedux[]) { + const providers = relays.flatMap((country) => + country.cities.flatMap((city) => city.relays.map((relay) => relay.provider)), + ); + return removeDuplicates(providers).sort((a, b) => a.localeCompare(b)); +} + function providersSelector(state: IReduxState): Record<string, boolean> { const providerConstraint = 'normal' in state.settings.relaySettings ? state.settings.relaySettings.normal.providers : []; @@ -106,14 +161,11 @@ function providersSelector(state: IReduxState): Record<string, boolean> { const relays = state.settings.relayLocations.concat( state.settings.bridgeState === 'on' ? state.settings.bridgeLocations : [], ); - const providers = relays.flatMap((country) => - country.cities.flatMap((city) => city.relays.map((relay) => relay.provider)), - ); - const uniqueProviders = removeDuplicates(providers).sort((a, b) => a.localeCompare(b)); + const providers = providersFromRelays(relays); // Empty containt array means that all providers are selected. No selection isn't possible. return Object.fromEntries( - uniqueProviders.map((provider) => [ + providers.map((provider) => [ provider, providerConstraint.length === 0 || providerConstraint.includes(provider), ]), @@ -126,6 +178,7 @@ const StyledSelector = (styled(Selector)({ interface IFilterByOwnershipProps { ownership: Ownership; + availableOptions: Ownership[]; setOwnership: (ownership: Ownership) => void; } @@ -133,21 +186,22 @@ function FilterByOwnership(props: IFilterByOwnershipProps) { const [expanded, , , toggleExpanded] = useBoolean(false); const values = useMemo( - () => [ - { - label: messages.gettext('Any'), - value: Ownership.any, - }, - { - label: messages.pgettext('filter-view', 'Mullvad owned only'), - value: Ownership.mullvadOwned, - }, - { - label: messages.pgettext('filter-view', 'Rented only'), - value: Ownership.rented, - }, - ], - [], + () => + [ + { + label: messages.gettext('Any'), + value: Ownership.any, + }, + { + label: messages.pgettext('filter-view', 'Mullvad owned only'), + value: Ownership.mullvadOwned, + }, + { + label: messages.pgettext('filter-view', 'Rented only'), + value: Ownership.rented, + }, + ].filter((option) => props.availableOptions.includes(option.value)), + [props.availableOptions], ); return ( @@ -172,6 +226,7 @@ function FilterByOwnership(props: IFilterByOwnershipProps) { interface IFilterByProviderProps { providers: Record<string, boolean>; + availableOptions: string[]; setProviders: (providers: (previous: Record<string, boolean>) => Record<string, boolean>) => void; } @@ -208,9 +263,11 @@ function FilterByProvider(props: IFilterByProviderProps) { checked={Object.values(props.providers).every((value) => value)} onChange={toggleAll} /> - {Object.entries(props.providers).map(([provider, checked]) => ( - <CheckboxRow key={provider} label={provider} checked={checked} onChange={onToggle} /> - ))} + {Object.entries(props.providers) + .filter(([provider]) => props.availableOptions.includes(provider)) + .map(([provider, checked]) => ( + <CheckboxRow key={provider} label={provider} checked={checked} onChange={onToggle} /> + ))} </Accordion> </> ); |
