diff options
| author | Oskar <oskar@mullvad.net> | 2024-10-22 15:32:24 +0200 |
|---|---|---|
| committer | Oskar <oskar@mullvad.net> | 2024-10-22 15:32:24 +0200 |
| commit | 1ec8c9d6c4dc799fb0c6479cdc9dad438bf37129 (patch) | |
| tree | 43caf1e9884496f64c2333648c16381eaa1cf18a /gui/src/renderer/components/select-location | |
| parent | 6533665d1fdf9a97e6ea1e36f01ee2f293b4338c (diff) | |
| parent | cf62ffdd17eac7958977895f098742c704aa3047 (diff) | |
| download | mullvadvpn-1ec8c9d6c4dc799fb0c6479cdc9dad438bf37129.tar.xz mullvadvpn-1ec8c9d6c4dc799fb0c6479cdc9dad438bf37129.zip | |
Merge branch 'add-react-linter-rules'
Diffstat (limited to 'gui/src/renderer/components/select-location')
11 files changed, 151 insertions, 89 deletions
diff --git a/gui/src/renderer/components/select-location/CustomListDialogs.tsx b/gui/src/renderer/components/select-location/CustomListDialogs.tsx index 4f97706210..4cc42c0623 100644 --- a/gui/src/renderer/components/select-location/CustomListDialogs.tsx +++ b/gui/src/renderer/components/select-location/CustomListDialogs.tsx @@ -13,7 +13,7 @@ import { messages } from '../../../shared/gettext'; import log from '../../../shared/logging'; import { useAppContext } from '../../context'; import { formatHtml } from '../../lib/html-formatter'; -import { useBoolean } from '../../lib/utilityHooks'; +import { useBoolean } from '../../lib/utility-hooks'; import { useSelector } from '../../redux/store'; import * as AppButton from '../AppButton'; import * as Cell from '../cell'; @@ -34,6 +34,8 @@ interface AddToListDialogProps { // Dialog that displays list of custom lists when adding location to custom list. export function AddToListDialog(props: AddToListDialogProps) { + const { hide } = props; + const { updateCustomList } = useAppContext(); const customLists = useSelector((state) => state.settings.customLists); @@ -51,9 +53,9 @@ export function AddToListDialog(props: AddToListDialogProps) { log.error(`Failed to edit custom list ${list.id}: ${error.message}`); } - props.hide(); + hide(); }, - [location, updateCustomList], + [hide, props.location, updateCustomList], ); let locationType: string; @@ -114,7 +116,9 @@ interface SelectListProps { } function SelectList(props: SelectListProps) { - const onAdd = useCallback(() => props.add(props.list), [props.list]); + const { add } = props; + + const onAdd = useCallback(() => add(props.list), [add, props.list]); // List should be disabled if location already is in list. const disabled = props.list.locations.some((location) => @@ -144,6 +148,8 @@ interface EditListProps { // Dialog for changing the name of a custom list. export function EditListDialog(props: EditListProps) { + const { hide } = props; + const { updateCustomList } = useAppContext(); const [newName, setNewName] = useState(props.list.name); @@ -160,20 +166,23 @@ export function EditListDialog(props: EditListProps) { if (result && result.type === 'name already exists') { setError(); } else { - props.hide(); + hide(); } } catch (e) { const error = e as Error; log.error(`Failed to edit custom list ${props.list.id}: ${error.message}`); } } - }, [props.list, newName, props.hide]); + }, [newNameValid, props.list, newNameTrimmed, updateCustomList, setError, hide]); // Errors should be reset when editing the value - const onChange = useCallback((value: string) => { - setNewName(value); - unsetError(); - }, []); + const onChange = useCallback( + (value: string) => { + setNewName(value); + unsetError(); + }, + [unsetError], + ); return ( <ModalAlert @@ -215,10 +224,12 @@ interface DeleteConfirmDialogProps { // Dialog for changing the name of a custom list. export function DeleteConfirmDialog(props: DeleteConfirmDialogProps) { + const { confirm: propsConfirm, hide } = props; + const confirm = useCallback(() => { - props.confirm(); - props.hide(); - }, []); + propsConfirm(); + hide(); + }, [hide, propsConfirm]); return ( <ModalAlert diff --git a/gui/src/renderer/components/select-location/CustomLists.tsx b/gui/src/renderer/components/select-location/CustomLists.tsx index 1cbb692b1b..1f4c77ed6b 100644 --- a/gui/src/renderer/components/select-location/CustomLists.tsx +++ b/gui/src/renderer/components/select-location/CustomLists.tsx @@ -6,7 +6,7 @@ import { CustomListError, CustomLists, RelayLocation } from '../../../shared/dae import { messages } from '../../../shared/gettext'; import log from '../../../shared/logging'; import { useAppContext } from '../../context'; -import { useBoolean, useStyledRef } from '../../lib/utilityHooks'; +import { useBoolean, useStyledRef } from '../../lib/utility-hooks'; import Accordion from '../Accordion'; import * as Cell from '../cell'; import { measurements } from '../common-styles'; @@ -77,15 +77,18 @@ export default function CustomLists(props: CustomListsProps) { const { searchTerm } = useSelectLocationContext(); const { customLists } = useRelayListContext(); - const createList = useCallback(async (name: string): Promise<void | CustomListError> => { - const result = await createCustomList(name); - // If an error is returned it should be passed as the return value. - if (result) { - return result; - } + const createList = useCallback( + async (name: string): Promise<void | CustomListError> => { + const result = await createCustomList(name); + // If an error is returned it should be passed as the return value. + if (result) { + return result; + } - hideAddList(); - }, []); + hideAddList(); + }, + [createCustomList, hideAddList], + ); if (searchTerm !== '' && !customLists.some((list) => list.visible)) { return null; @@ -121,6 +124,8 @@ interface AddListFormProps { } function AddListForm(props: AddListFormProps) { + const { onCreateList, cancel } = props; + const [name, setName] = useState(''); const nameTrimmed = name.trim(); const nameValid = nameTrimmed !== ''; @@ -129,15 +134,18 @@ function AddListForm(props: AddListFormProps) { const inputRef = useStyledRef<HTMLInputElement>(); // Errors should be reset when editing the value - const onChange = useCallback((value: string) => { - setName(value); - unsetError(); - }, []); + const onChange = useCallback( + (value: string) => { + setName(value); + unsetError(); + }, + [unsetError], + ); const createList = useCallback(async () => { if (nameValid) { try { - const result = await props.onCreateList(nameTrimmed); + const result = await onCreateList(nameTrimmed); if (result) { setError(); } @@ -146,16 +154,16 @@ function AddListForm(props: AddListFormProps) { log.error('Failed to create list:', error.message); } } - }, [name, props.onCreateList, nameValid]); + }, [nameValid, onCreateList, nameTrimmed, setError]); const onBlur = useCallback( (event: React.FocusEvent<HTMLInputElement>) => { // Only cancel if losing focus to something else than the contents of the row container. if (!event.relatedTarget || !containerRef.current?.contains(event.relatedTarget)) { - props.cancel(); + cancel(); } }, - [props.cancel], + [containerRef, cancel], ); const onTransitionEnd = useCallback(() => { @@ -168,7 +176,7 @@ function AddListForm(props: AddListFormProps) { if (props.visible) { inputRef.current?.focus(); } - }, [props.visible]); + }, [inputRef, props.visible]); return ( <BackAction disabled={!props.visible} action={props.cancel}> @@ -211,6 +219,8 @@ interface CustomListsImplProps { } function CustomListsImpl(props: CustomListsImplProps) { + const { onSelect: propsOnSelect } = props; + const { customLists, expandLocation, collapseLocation, onBeforeExpand } = useRelayListContext(); const { resetHeight } = useScrollPositionContext(); @@ -221,9 +231,9 @@ function CustomListsImpl(props: CustomListsImplProps) { // Only the geographical part should be sent to the daemon when setting a location. delete location.customList; } - props.onSelect(location); + propsOnSelect(location); }, - [props.onSelect], + [propsOnSelect], ); return ( diff --git a/gui/src/renderer/components/select-location/LocationRow.tsx b/gui/src/renderer/components/select-location/LocationRow.tsx index 41f3aa7152..a47c1dd646 100644 --- a/gui/src/renderer/components/select-location/LocationRow.tsx +++ b/gui/src/renderer/components/select-location/LocationRow.tsx @@ -9,7 +9,7 @@ import { import { messages } from '../../../shared/gettext'; import log from '../../../shared/logging'; import { useAppContext } from '../../context'; -import { useBoolean, useStyledRef } from '../../lib/utilityHooks'; +import { useBoolean, useStyledRef } from '../../lib/utility-hooks'; import { useSelector } from '../../redux/store'; import Accordion from '../Accordion'; import * as Cell from '../cell'; @@ -55,6 +55,8 @@ interface IProps<C extends LocationSpecification> { // Renders the rows and its children for countries, cities and relays function LocationRow<C extends LocationSpecification>(props: IProps<C>) { + const { onSelect, onWillExpand: propsOnWillExpand } = props; + const hasChildren = getLocationChildren(props.source).some((child) => child.visible); const buttonRef = useStyledRef<HTMLButtonElement>(); const userInvokedExpand = useRef(false); @@ -79,19 +81,19 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { const handleClick = useCallback(() => { if (!props.source.selected) { - props.onSelect(props.source.location); + onSelect(props.source.location); } - }, [props.onSelect, props.source.location, props.source.selected]); + }, [onSelect, props.source.location, props.source.selected]); const onWillExpand = useCallback( (nextHeight: number) => { const buttonRect = buttonRef.current?.getBoundingClientRect(); if (expanded !== undefined && buttonRect) { - props.onWillExpand(buttonRect, nextHeight, userInvokedExpand.current); + propsOnWillExpand(buttonRect, nextHeight, userInvokedExpand.current); userInvokedExpand.current = false; } }, - [props.onWillExpand, expanded], + [buttonRef, expanded, propsOnWillExpand], ); const onRemoveFromList = useCallback(async () => { @@ -116,7 +118,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { } } } - }, [customLists, props.source.location]); + }, [customLists, props.source.location, updateCustomList]); // Remove an entire custom list. const confirmRemoveCustomList = useCallback(async () => { @@ -130,7 +132,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { ); } } - }, [props.source.location.customList]); + }, [deleteCustomList, props.source.location.customList]); if (!props.source.visible) { return null; diff --git a/gui/src/renderer/components/select-location/RelayListContext.tsx b/gui/src/renderer/components/select-location/RelayListContext.tsx index 86fb8de22b..3165a95824 100644 --- a/gui/src/renderer/components/select-location/RelayListContext.tsx +++ b/gui/src/renderer/components/select-location/RelayListContext.tsx @@ -13,7 +13,8 @@ import { useNormalBridgeSettings, useNormalRelaySettings, useTunnelProtocol, -} from '../../lib/utilityHooks'; +} from '../../lib/relay-settings-hooks'; +import { useEffectEvent } from '../../lib/utility-hooks'; import { IRelayLocationCountryRedux } from '../../redux/settings/reducers'; import { useSelector } from '../../redux/store'; import { useCustomListsRelayList } from './custom-list-helpers'; @@ -83,7 +84,7 @@ export function RelayListContextProvider(props: RelayListContextProviderProps) { tunnelProtocol, relaySettings, ); - }, [fullRelayList, locationType, relaySettings?.tunnelProtocol]); + }, [fullRelayList, locationType, relaySettings, tunnelProtocol]); const relayListForDaita = useMemo(() => { return filterLocationsByDaita( @@ -94,7 +95,14 @@ export function RelayListContextProvider(props: RelayListContextProviderProps) { relaySettings?.tunnelProtocol ?? 'any', relaySettings?.wireguard.useMultihop ?? false, ); - }, [daita, directOnly, locationType, relaySettings, relayListForEndpointType]); + }, [ + daita, + directOnly, + locationType, + relayListForEndpointType, + relaySettings?.tunnelProtocol, + relaySettings?.wireguard.useMultihop, + ]); // Filters the relays to only keep the relays matching the currently selected filters, e.g. // ownership and providers @@ -280,7 +288,7 @@ function useExpandedLocations(filteredLocations: Array<IRelayLocationCountryRedu scrollIntoView(locationRect); } }, - [], + [scrollIntoView, spacePreAllocationViewRef], ); // Expand search results when searching @@ -298,15 +306,19 @@ function useExpandedLocations(filteredLocations: Array<IRelayLocationCountryRedu [relaySettings, bridgeSettings, locationType, filteredLocations], ); + const expandLocationsForSearch = useEffectEvent( + (filteredLocations: Array<IRelayLocationCountryRedux>) => { + if (searchTerm !== '') { + setExpandedLocations((expandedLocations) => ({ + ...expandedLocations, + [locationType]: getLocationsExpandedBySearch(filteredLocations, searchTerm), + })); + } + }, + ); + // Expand locations when filters are changed - useEffect(() => { - if (searchTerm !== '') { - setExpandedLocations((expandedLocations) => ({ - ...expandedLocations, - [locationType]: getLocationsExpandedBySearch(filteredLocations, searchTerm), - })); - } - }, [filteredLocations]); + useEffect(() => expandLocationsForSearch(filteredLocations), [filteredLocations]); return { expandedLocations: expandedLocationsMap[locationType], diff --git a/gui/src/renderer/components/select-location/ScopeBar.tsx b/gui/src/renderer/components/select-location/ScopeBar.tsx index bbe4935993..28312da19e 100644 --- a/gui/src/renderer/components/select-location/ScopeBar.tsx +++ b/gui/src/renderer/components/select-location/ScopeBar.tsx @@ -57,11 +57,13 @@ interface IScopeBarItemProps { } export function ScopeBarItem(props: IScopeBarItemProps) { + const { onClick: propOnClick } = props; + const onClick = useCallback(() => { if (props.index !== undefined) { - props.onClick?.(props.index); + propOnClick?.(props.index); } - }, [props.onClick, props.index]); + }, [propOnClick, props.index]); return props.index !== undefined ? ( <StyledScopeBarItem selected={props.selected} onClick={onClick}> diff --git a/gui/src/renderer/components/select-location/ScrollPositionContext.tsx b/gui/src/renderer/components/select-location/ScrollPositionContext.tsx index d27e252427..55ee8dae97 100644 --- a/gui/src/renderer/components/select-location/ScrollPositionContext.tsx +++ b/gui/src/renderer/components/select-location/ScrollPositionContext.tsx @@ -2,7 +2,8 @@ import { Action } from 'history'; import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; import { useHistory } from '../../lib/history'; -import { useNormalRelaySettings, useStyledRef } from '../../lib/utilityHooks'; +import { useNormalRelaySettings } from '../../lib/relay-settings-hooks'; +import { useStyledRef } from '../../lib/utility-hooks'; import { CustomScrollbarsRef } from '../CustomScrollbars'; import { LocationType } from './select-location-types'; import { useSelectLocationContext } from './SelectLocationContainer'; @@ -69,7 +70,10 @@ export function ScrollPositionContextProvider(props: ScrollPositionContextProps) scrollViewRef.current?.scrollIntoView(rect); }, []); - const resetHeight = useCallback(() => spacePreAllocationViewRef.current?.reset(), []); + const resetHeight = useCallback( + () => spacePreAllocationViewRef.current?.reset(), + [spacePreAllocationViewRef], + ); const value = useMemo( () => ({ @@ -82,7 +86,13 @@ export function ScrollPositionContextProvider(props: ScrollPositionContextProps) scrollIntoView, resetHeight, }), - [saveScrollPosition, resetScrollPositions], + [ + spacePreAllocationViewRef, + saveScrollPosition, + resetScrollPositions, + scrollIntoView, + resetHeight, + ], ); // Restore the scroll position when parameters change diff --git a/gui/src/renderer/components/select-location/SelectLocation.tsx b/gui/src/renderer/components/select-location/SelectLocation.tsx index 438fdc430d..506c836548 100644 --- a/gui/src/renderer/components/select-location/SelectLocation.tsx +++ b/gui/src/renderer/components/select-location/SelectLocation.tsx @@ -8,8 +8,8 @@ import { useRelaySettingsUpdater } from '../../lib/constraint-updater'; import { daitaFilterActive, filterSpecialLocations } from '../../lib/filter-locations'; import { useHistory } from '../../lib/history'; import { formatHtml } from '../../lib/html-formatter'; +import { useNormalRelaySettings } from '../../lib/relay-settings-hooks'; import { RoutePath } from '../../lib/routes'; -import { useNormalRelaySettings } from '../../lib/utilityHooks'; import { useSelector } from '../../redux/store'; import * as Cell from '../cell'; import { useFilteredProviders } from '../Filter'; @@ -107,7 +107,7 @@ export default function SelectLocation() { saveScrollPosition(); setLocationType(locationType); }, - [saveScrollPosition], + [saveScrollPosition, setLocationType], ); const updateSearchTerm = useCallback( @@ -122,7 +122,7 @@ export default function SelectLocation() { setSearchTerm(value); } }, - [resetScrollPositions, expandSearchResults], + [expandSearchResults, setSearchTerm, resetScrollPositions], ); const showOwnershipFilter = ownership !== Ownership.any; diff --git a/gui/src/renderer/components/select-location/SelectLocationContainer.tsx b/gui/src/renderer/components/select-location/SelectLocationContainer.tsx index 1d0e5251f7..66bebdf1b0 100644 --- a/gui/src/renderer/components/select-location/SelectLocationContainer.tsx +++ b/gui/src/renderer/components/select-location/SelectLocationContainer.tsx @@ -29,7 +29,7 @@ export default function SelectLocationContainer() { const value = useMemo( () => ({ locationType, setLocationType: setSelectLocationView, searchTerm, setSearchTerm }), - [locationType, searchTerm], + [locationType, searchTerm, setSelectLocationView], ); return ( diff --git a/gui/src/renderer/components/select-location/SpecialLocationList.tsx b/gui/src/renderer/components/select-location/SpecialLocationList.tsx index f4ccf13ec2..e4d105d635 100644 --- a/gui/src/renderer/components/select-location/SpecialLocationList.tsx +++ b/gui/src/renderer/components/select-location/SpecialLocationList.tsx @@ -45,11 +45,12 @@ interface SpecialLocationRowProps<T> { } function SpecialLocationRow<T>(props: SpecialLocationRowProps<T>) { + const { onSelect: propsOnSelect } = props; const onSelect = useCallback(() => { if (!props.source.selected) { - props.onSelect(props.source.value); + propsOnSelect(props.source.value); } - }, [props.source.selected, props.onSelect, props.source.value]); + }, [props.source, propsOnSelect]); const innerProps: SpecialLocationRowInnerProps<T> = { ...props, @@ -105,7 +106,7 @@ const StyledInfoButton = styled(StyledHoverInfoButton)({ display: 'block' }); export function CustomBridgeLocationRow( props: SpecialLocationRowInnerProps<SpecialBridgeLocationType>, ) { - const history = useHistory(); + const { push } = useHistory(); const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); const bridgeConfigured = bridgeSettings.custom !== undefined; @@ -114,7 +115,7 @@ export function CustomBridgeLocationRow( const selectedRef = props.source.selected ? props.selectedElementRef : undefined; const background = getButtonColor(props.source.selected, 0, props.source.disabled); - const navigate = useCallback(() => history.push(RoutePath.editCustomBridge), [history.push]); + const navigate = useCallback(() => push(RoutePath.editCustomBridge), [push]); return ( <StyledLocationRowContainerWithMargin ref={selectedRef} disabled={props.source.disabled}> diff --git a/gui/src/renderer/components/select-location/custom-list-helpers.ts b/gui/src/renderer/components/select-location/custom-list-helpers.ts index 5cb6d695b8..3f6149cd7f 100644 --- a/gui/src/renderer/components/select-location/custom-list-helpers.ts +++ b/gui/src/renderer/components/select-location/custom-list-helpers.ts @@ -46,7 +46,15 @@ export function useCustomListsRelayList( expandedLocations, ), ), - [customLists, relayList, selectedLocation, disabledLocation, expandedLocations], + [ + customLists, + relayList, + searchTerm, + preventDueToCustomBridgeSelected, + selectedLocation, + disabledLocation, + expandedLocations, + ], ); } diff --git a/gui/src/renderer/components/select-location/select-location-hooks.ts b/gui/src/renderer/components/select-location/select-location-hooks.ts index 027185e416..fbecc4db5d 100644 --- a/gui/src/renderer/components/select-location/select-location-hooks.ts +++ b/gui/src/renderer/components/select-location/select-location-hooks.ts @@ -30,7 +30,7 @@ export function useOnSelectExitLocation() { await onSelectLocation({ normal: settings }); await connectTunnel(); }, - [history, relaySettingsModifier], + [connectTunnel, history, onSelectLocation, relaySettingsModifier], ); const onSelectSpecial = useCallback((_location: undefined) => { @@ -54,7 +54,7 @@ export function useOnSelectEntryLocation() { }); await onSelectLocation({ normal: settings }); }, - [relaySettingsModifier], + [onSelectLocation, relaySettingsModifier, setLocationType], ); const onSelectSpecial = useCallback( @@ -66,7 +66,7 @@ export function useOnSelectEntryLocation() { }); await onSelectLocation({ normal: settings }); }, - [relaySettingsModifier], + [onSelectLocation, relaySettingsModifier, setLocationType], ); return [onSelectRelay, onSelectSpecial] as const; @@ -75,14 +75,17 @@ export function useOnSelectEntryLocation() { function useOnSelectLocation() { const { setRelaySettings } = useAppContext(); - return useCallback(async (relaySettings: RelaySettings) => { - try { - await setRelaySettings(relaySettings); - } catch (e) { - const error = e as Error; - log.error(`Failed to select the location: ${error.message}`); - } - }, []); + return useCallback( + async (relaySettings: RelaySettings) => { + try { + await setRelaySettings(relaySettings); + } catch (e) { + const error = e as Error; + log.error(`Failed to select the location: ${error.message}`); + } + }, + [setRelaySettings], + ); } export function useOnSelectBridgeLocation() { @@ -90,17 +93,20 @@ export function useOnSelectBridgeLocation() { const { setLocationType } = useSelectLocationContext(); const bridgeSettingsModifier = useBridgeSettingsModifier(); - const setLocation = useCallback(async (bridgeUpdate: BridgeSettings) => { - if (bridgeUpdate) { - setLocationType(LocationType.exit); - try { - await updateBridgeSettings(bridgeUpdate); - } catch (e) { - const error = e as Error; - log.error(`Failed to select the bridge location: ${error.message}`); + const setLocation = useCallback( + async (bridgeUpdate: BridgeSettings) => { + if (bridgeUpdate) { + setLocationType(LocationType.exit); + try { + await updateBridgeSettings(bridgeUpdate); + } catch (e) { + const error = e as Error; + log.error(`Failed to select the bridge location: ${error.message}`); + } } - } - }, []); + }, + [setLocationType, updateBridgeSettings], + ); const onSelectRelay = useCallback( (location: RelayLocation) => { @@ -112,7 +118,7 @@ export function useOnSelectBridgeLocation() { }), ); }, - [bridgeSettingsModifier], + [bridgeSettingsModifier, setLocation], ); const onSelectSpecial = useCallback( @@ -135,7 +141,7 @@ export function useOnSelectBridgeLocation() { ); } }, - [bridgeSettingsModifier], + [bridgeSettingsModifier, setLocation], ); return [onSelectRelay, onSelectSpecial] as const; |
