summaryrefslogtreecommitdiffhomepage
path: root/desktop/packages/mullvad-vpn/src/renderer/lib/api-access-methods.ts
blob: d52d9ab0e22ef0475ce4bbb830a73875dc1a21c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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 './utility-hooks';

export function useApiAccessMethodTest(
  autoReset = true,
  minDuration = 0,
): [
  boolean,
  boolean | undefined,
  (method: CustomProxy | string) => Promise<boolean | void>,
  () => void,
] {
  const { testApiAccessMethodById, testCustomApiAccessMethod } = useAppContext();
  const delayScheduler = useScheduler();

  // Whether or not the method is currently being tested.
  const [testing, setTesting, unsetTesting] = useBoolean();
  const [testResult, setTestResult] = useState<boolean>();
  // We keep the promise for the most recent test to compare it when we receive the results to know
  // if it's canceled or not.
  const lastTestPromise = useRef<Promise<boolean>>(undefined);

  // A few seconds after the test has finished the result should not be displayed anymore. This
  // scheduler is used to clear it.
  const testResultResetScheduler = useScheduler();

  const testApiAccessMethod = useCallback(
    async (method: CustomProxy | string) => {
      testResultResetScheduler.cancel();
      setTestResult(undefined);

      setTesting();
      let reachable;
      let testPromise;

      const submitTimestamp = Date.now();
      try {
        testPromise =
          typeof method === 'string'
            ? testApiAccessMethodById(method)
            : testCustomApiAccessMethod(method);

        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),
        );
      }

      if (testPromise !== lastTestPromise.current) {
        return;
      }

      setTestResult(reachable);
      unsetTesting();

      if (autoReset) {
        testResultResetScheduler.schedule(() => setTestResult(undefined), 5000);
      }

      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];
}