summaryrefslogtreecommitdiffhomepage
path: root/gui/src/renderer
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2022-07-19 10:55:29 +0200
committerOskar Nyberg <oskar@mullvad.net>2022-07-20 13:48:15 +0200
commit057bfc81ab6cc95cfec6fc7d9049bf87bb405097 (patch)
tree6480de1a6f5a5d115ac64188cfd546cbcf94f486 /gui/src/renderer
parent163f3370d7655d099636a58869d655547b0400f5 (diff)
downloadmullvadvpn-057bfc81ab6cc95cfec6fc7d9049bf87bb405097.tar.xz
mullvadvpn-057bfc81ab6cc95cfec6fc7d9049bf87bb405097.zip
Make filter update when others are changed
Diffstat (limited to 'gui/src/renderer')
-rw-r--r--gui/src/renderer/components/Filter.tsx125
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>
</>
);