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/lib | |
| 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/lib')
| -rw-r--r-- | gui/src/renderer/lib/3dmap.ts | 4 | ||||
| -rw-r--r-- | gui/src/renderer/lib/actionsHook.ts | 5 | ||||
| -rw-r--r-- | gui/src/renderer/lib/api-access-methods.ts | 84 | ||||
| -rw-r--r-- | gui/src/renderer/lib/constraint-updater.ts | 2 | ||||
| -rw-r--r-- | gui/src/renderer/lib/history.tsx | 8 | ||||
| -rw-r--r-- | gui/src/renderer/lib/relay-settings-hooks.ts | 25 | ||||
| -rw-r--r-- | gui/src/renderer/lib/utility-hooks.ts | 76 | ||||
| -rw-r--r-- | gui/src/renderer/lib/utilityHooks.ts | 94 |
8 files changed, 164 insertions, 134 deletions
diff --git a/gui/src/renderer/lib/3dmap.ts b/gui/src/renderer/lib/3dmap.ts index ec4def0744..d0e130e4c8 100644 --- a/gui/src/renderer/lib/3dmap.ts +++ b/gui/src/renderer/lib/3dmap.ts @@ -666,8 +666,8 @@ function getProjectionMatrix(gl: WebGL2RenderingContext): mat4 { // Create a perspective matrix, a special matrix that is // used to simulate the distortion of perspective in a camera. const fieldOfView = (angleOfView / 180) * Math.PI; // in radians - // @ts-ignore - const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; + const canvas = gl.canvas as HTMLCanvasElement; + const aspect = canvas.clientWidth / canvas.clientHeight; const zNear = 0.1; const zFar = 10; const projectionMatrix = mat4.create(); diff --git a/gui/src/renderer/lib/actionsHook.ts b/gui/src/renderer/lib/actionsHook.ts index 2597aee422..fc046d0a66 100644 --- a/gui/src/renderer/lib/actionsHook.ts +++ b/gui/src/renderer/lib/actionsHook.ts @@ -4,6 +4,9 @@ import { ActionCreatorsMapObject, bindActionCreators } from 'redux'; export default function useActions<A, M extends ActionCreatorsMapObject<A>>(actionCreator: M) { const dispatch = useDispatch(); - const actions = useMemo(() => bindActionCreators(actionCreator, dispatch), [dispatch]); + const actions = useMemo( + () => bindActionCreators(actionCreator, dispatch), + [actionCreator, dispatch], + ); return actions; } diff --git a/gui/src/renderer/lib/api-access-methods.ts b/gui/src/renderer/lib/api-access-methods.ts index fd78d1484a..90d406cc3d 100644 --- a/gui/src/renderer/lib/api-access-methods.ts +++ b/gui/src/renderer/lib/api-access-methods.ts @@ -3,7 +3,7 @@ import { useCallback, useRef, useState } from 'react'; import { CustomProxy } from '../../shared/daemon-rpc-types'; import { useScheduler } from '../../shared/scheduler'; import { useAppContext } from '../context'; -import { useBoolean } from './utilityHooks'; +import { useBoolean } from './utility-hooks'; export function useApiAccessMethodTest( autoReset = true, @@ -28,54 +28,66 @@ export function useApiAccessMethodTest( // scheduler is used to clear it. const testResultResetScheduler = useScheduler(); - const testApiAccessMethod = useCallback(async (method: CustomProxy | string) => { - testResultResetScheduler.cancel(); - setTestResult(undefined); + const testApiAccessMethod = useCallback( + async (method: CustomProxy | string) => { + testResultResetScheduler.cancel(); + setTestResult(undefined); - setTesting(); - let reachable; - let testPromise; + setTesting(); + let reachable; + let testPromise; - const submitTimestamp = Date.now(); - try { - testPromise = - typeof method === 'string' - ? testApiAccessMethodById(method) - : testCustomApiAccessMethod(method); + const submitTimestamp = Date.now(); + try { + testPromise = + typeof method === 'string' + ? testApiAccessMethodById(method) + : testCustomApiAccessMethod(method); - lastTestPromise.current = testPromise; - reachable = await testPromise; - } catch { - reachable = false; - } + lastTestPromise.current = testPromise; + reachable = await testPromise; + } catch { + reachable = false; + } - // Make sure the loading text is displayed for at least `minDuration` milliseconds. - const submitDuration = Date.now() - submitTimestamp; - if (submitDuration < minDuration) { - await new Promise<void>((resolve) => - delayScheduler.schedule(resolve, minDuration - submitDuration), - ); - } + // Make sure the loading text is displayed for at least `minDuration` milliseconds. + const submitDuration = Date.now() - submitTimestamp; + if (submitDuration < minDuration) { + await new Promise<void>((resolve) => + delayScheduler.schedule(resolve, minDuration - submitDuration), + ); + } - if (testPromise !== lastTestPromise.current) { - return; - } + if (testPromise !== lastTestPromise.current) { + return; + } - setTestResult(reachable); - unsetTesting(); + setTestResult(reachable); + unsetTesting(); - if (autoReset) { - testResultResetScheduler.schedule(() => setTestResult(undefined), 5000); - } + if (autoReset) { + testResultResetScheduler.schedule(() => setTestResult(undefined), 5000); + } - return reachable; - }, []); + return reachable; + }, + [ + autoReset, + delayScheduler, + minDuration, + setTesting, + testApiAccessMethodById, + testCustomApiAccessMethod, + testResultResetScheduler, + unsetTesting, + ], + ); const resetTestResult = useCallback(() => { lastTestPromise.current = undefined; unsetTesting(); setTestResult(undefined); - }, []); + }, [unsetTesting]); return [testing, testResult, testApiAccessMethod, resetTestResult]; } diff --git a/gui/src/renderer/lib/constraint-updater.ts b/gui/src/renderer/lib/constraint-updater.ts index 73fbdd1336..6ea021ece9 100644 --- a/gui/src/renderer/lib/constraint-updater.ts +++ b/gui/src/renderer/lib/constraint-updater.ts @@ -16,7 +16,7 @@ import { NormalRelaySettingsRedux, } from '../redux/settings/reducers'; import { useSelector } from '../redux/store'; -import { useNormalRelaySettings } from './utilityHooks'; +import { useNormalRelaySettings } from './relay-settings-hooks'; export function wrapRelaySettingsOrDefault( relaySettings?: NormalRelaySettingsRedux, diff --git a/gui/src/renderer/lib/history.tsx b/gui/src/renderer/lib/history.tsx index 741c298da6..6d92a0e88c 100644 --- a/gui/src/renderer/lib/history.tsx +++ b/gui/src/renderer/lib/history.tsx @@ -82,6 +82,14 @@ export default class History { return history; } + public recordScrollPosition(position: [number, number]) { + this.location.state.scrollPosition = position; + } + + public recordSectionExpandedState(id: string, expanded: boolean) { + this.location.state.expandedSections[id] = expanded; + } + public get location(): Location<LocationState> { return this.entries[this.index]; } diff --git a/gui/src/renderer/lib/relay-settings-hooks.ts b/gui/src/renderer/lib/relay-settings-hooks.ts new file mode 100644 index 0000000000..14fe99849d --- /dev/null +++ b/gui/src/renderer/lib/relay-settings-hooks.ts @@ -0,0 +1,25 @@ +import { LiftedConstraint, TunnelProtocol } from '../../shared/daemon-rpc-types'; +import { useSelector } from '../redux/store'; + +export function useNormalRelaySettings() { + const relaySettings = useSelector((state) => state.settings.relaySettings); + return 'normal' in relaySettings ? relaySettings.normal : undefined; +} + +// Some features are considered core privacy features and when enabled prevent OpenVPN from being +// used. This hook returns the tunnelprotocol with the exception that it always returns WireGuard +// when any of those features are enabled. +export function useTunnelProtocol(): LiftedConstraint<TunnelProtocol> { + const relaySettings = useNormalRelaySettings(); + const multihop = relaySettings?.wireguard.useMultihop ?? false; + const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); + const quantumResistant = useSelector((state) => state.settings.wireguard.quantumResistant); + const openVpnDisabled = daita || multihop || quantumResistant; + + return openVpnDisabled ? 'wireguard' : (relaySettings?.tunnelProtocol ?? 'any'); +} + +export function useNormalBridgeSettings() { + const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); + return bridgeSettings.normal; +} diff --git a/gui/src/renderer/lib/utility-hooks.ts b/gui/src/renderer/lib/utility-hooks.ts new file mode 100644 index 0000000000..1efc49c804 --- /dev/null +++ b/gui/src/renderer/lib/utility-hooks.ts @@ -0,0 +1,76 @@ +import React, { useCallback, useEffect, useInsertionEffect, useRef, useState } from 'react'; + +export function useMounted() { + const mountedRef = useRef(false); + const isMounted = useCallback(() => mountedRef.current, []); + + useEffect(() => { + mountedRef.current = true; + return () => { + mountedRef.current = false; + }; + }, []); + + return isMounted; +} + +export function useStyledRef<T>(): React.MutableRefObject<T> { + return useRef() as React.MutableRefObject<T>; +} + +export function useCombinedRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> { + return useRefCallback((element: T | null) => refs.forEach((ref) => assignToRef(element, ref))); +} + +export function assignToRef<T>(element: T | null, ref?: React.Ref<T>) { + if (typeof ref === 'function') { + ref(element); + } else if (ref && element) { + (ref as React.MutableRefObject<T>).current = element; + } +} + +export function useBoolean(initialValue = false) { + const [value, setValue] = useState(initialValue); + + const setTrue = useCallback(() => setValue(true), []); + const setFalse = useCallback(() => setValue(false), []); + const toggle = useCallback(() => setValue((value) => !value), []); + + return [value, setTrue, setFalse, toggle] as const; +} + +// This hook returns a function that can be used to force a rerender of a component, and +// additionally also returns a variable that can be used to trigger effects as a result. This is a +// hack and should be avoided unless there are no better ways. +export function useRerenderer(): [() => void, number] { + const [count, setCount] = useState(0); + const rerender = useCallback(() => setCount((count) => count + 1), []); + return [rerender, count]; +} + +type Fn<T extends unknown[], R> = (...args: T) => R; + +export function useEffectEvent<Args extends unknown[]>( + fn: Fn<Args, void | undefined | Promise<void | undefined>>, +): Fn<Args, void> { + const ref = useRef<Fn<Args, void>>(fn); + + useInsertionEffect(() => { + ref.current = fn; + }, [fn]); + + return useCallback((...args: Args) => ref.current(...args), []); +} + +// Alias for useEffectEvent, but with another name since the effect event is named after a very +// specific usecase. +export const useRefCallback = useEffectEvent; + +export function useLastDefinedValue<T>(value: T): T { + const [definedValue, setDefinedValue] = useState(value); + + useEffect(() => setDefinedValue((prev) => value ?? prev), [value]); + + return value ?? definedValue; +} diff --git a/gui/src/renderer/lib/utilityHooks.ts b/gui/src/renderer/lib/utilityHooks.ts deleted file mode 100644 index be1688f3f3..0000000000 --- a/gui/src/renderer/lib/utilityHooks.ts +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; - -import { LiftedConstraint, TunnelProtocol } from '../../shared/daemon-rpc-types'; -import { useSelector } from '../redux/store'; - -export function useMounted() { - const mountedRef = useRef(false); - const isMounted = useCallback(() => mountedRef.current, []); - - useEffect(() => { - mountedRef.current = true; - return () => { - mountedRef.current = false; - }; - }, []); - - return isMounted; -} - -export function useStyledRef<T>(): React.RefObject<T> { - return useRef() as React.RefObject<T>; -} - -export function useCombinedRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> { - return useCallback((element: T | null) => refs.forEach((ref) => assignToRef(element, ref)), []); -} - -export function assignToRef<T>(element: T | null, ref?: React.Ref<T>) { - if (typeof ref === 'function') { - ref(element); - } else if (ref && element) { - (ref as React.MutableRefObject<T>).current = element; - } -} - -export function useAsyncEffect( - effect: () => Promise<void | (() => void | Promise<void>)>, - dependencies: unknown[], -): void { - const isMounted = useMounted(); - - useEffect(() => { - const promise = effect(); - return () => { - void promise.then((destructor) => { - if (isMounted() && destructor) { - return destructor(); - } - }); - }; - }, dependencies); -} - -export function useBoolean(initialValue = false) { - const [value, setValue] = useState(initialValue); - - const setTrue = useCallback(() => setValue(true), []); - const setFalse = useCallback(() => setValue(false), []); - const toggle = useCallback(() => setValue((value) => !value), []); - - return [value, setTrue, setFalse, toggle] as const; -} - -export function useNormalRelaySettings() { - const relaySettings = useSelector((state) => state.settings.relaySettings); - return 'normal' in relaySettings ? relaySettings.normal : undefined; -} - -// Some features are considered core privacy features and when enabled prevent OpenVPN from being -// used. This hook returns the tunnelprotocol with the exception that it always returns WireGuard -// when any of those features are enabled. -export function useTunnelProtocol(): LiftedConstraint<TunnelProtocol> { - const relaySettings = useNormalRelaySettings(); - const multihop = relaySettings?.wireguard.useMultihop ?? false; - const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); - const quantumResistant = useSelector((state) => state.settings.wireguard.quantumResistant); - const openVpnDisabled = daita || multihop || quantumResistant; - - return openVpnDisabled ? 'wireguard' : (relaySettings?.tunnelProtocol ?? 'any'); -} - -export function useNormalBridgeSettings() { - const bridgeSettings = useSelector((state) => state.settings.bridgeSettings); - return bridgeSettings.normal; -} - -// This hook returns a function that can be used to force a rerender of a component, and -// additionally also returns a variable that can be used to trigger effects as a result. This is a -// hack and should be avoided unless there are no better ways. -export function useRerenderer(): [() => void, number] { - const [count, setCount] = useState(0); - const rerender = useCallback(() => setCount((count) => count + 1), []); - return [rerender, count]; -} |
