summaryrefslogtreecommitdiffhomepage
path: root/gui/src/renderer/lib
diff options
context:
space:
mode:
authorOskar <oskar@mullvad.net>2024-10-22 15:32:24 +0200
committerOskar <oskar@mullvad.net>2024-10-22 15:32:24 +0200
commit1ec8c9d6c4dc799fb0c6479cdc9dad438bf37129 (patch)
tree43caf1e9884496f64c2333648c16381eaa1cf18a /gui/src/renderer/lib
parent6533665d1fdf9a97e6ea1e36f01ee2f293b4338c (diff)
parentcf62ffdd17eac7958977895f098742c704aa3047 (diff)
downloadmullvadvpn-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.ts4
-rw-r--r--gui/src/renderer/lib/actionsHook.ts5
-rw-r--r--gui/src/renderer/lib/api-access-methods.ts84
-rw-r--r--gui/src/renderer/lib/constraint-updater.ts2
-rw-r--r--gui/src/renderer/lib/history.tsx8
-rw-r--r--gui/src/renderer/lib/relay-settings-hooks.ts25
-rw-r--r--gui/src/renderer/lib/utility-hooks.ts76
-rw-r--r--gui/src/renderer/lib/utilityHooks.ts94
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];
-}