All files / client/src/views/login/components KompassiLoginButton.tsx

72.72% Statements 16/22
75% Branches 6/8
75% Functions 3/4
72.72% Lines 16/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                      520x 71x 71x   71x                 71x   71x 63x 63x 63x 57x   6x   6x                 21x     21x 21x 21x                                                        
import { captureException } from "@sentry/react";
import { ReactElement, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useSearchParams } from "react-router";
import { z } from "zod";
import { Button } from "client/components/Button";
import { ErrorMessage } from "client/components/ErrorMessage";
import { ButtonStyle } from "client/components/componentStyles";
import { postKompassiLoginRedirect } from "client/services/loginServices";
import { LoginErrorMessage } from "client/views/login/loginThunks";
 
export const KompassiLoginButton = (): ReactElement => {
  const { t } = useTranslation();
  const [searchParams, setSearchParams] = useSearchParams();
 
  const [serverError, setServerError] = useState<LoginErrorMessage | null>(
    null,
  );
 
  // A second click would store a second state while the first request is still
  // in flight, and the page then leaves for whichever authorize URL resolves
  // last - which may carry the state that is no longer stored. A ref rather
  // than state on purpose: nothing renders from it, so a page restored from
  // the bfcache (Back from the Kompassi consent screen) can't come back stuck
  const redirecting = useRef(false);
 
  useEffect(() => {
    const error = searchParams.get("error");
    const result = z.enum(LoginErrorMessage).safeParse(error);
    if (!result.success) {
      return;
    }
    Eif (error) {
      // eslint-disable-next-line react-hooks/set-state-in-effect
      setServerError(result.data);
    }
  }, [searchParams]);
 
  return (
    <>
      <Button
        buttonStyle={ButtonStyle.PRIMARY}
        onClick={async () => {
          Iif (redirecting.current) {
            return;
          }
          redirecting.current = true;
          try {
            await postKompassiLoginRedirect();
          } catch (error) {
            // The request wrapper reports its own failures, so getting here
            // means storage refused to keep the login state. Nothing else
            // would notice: the click just appears to do nothing
            captureException(error);
          }
          // Only reached when no redirect happened: the success path never
          // resolves, because the page is unloading. A `finally` would say
          // this better, but the React Compiler declines any function
          // containing one
          redirecting.current = false;
        }}
      >
        {t("loginView.kompassiLogin")}
      </Button>
      {serverError && (
        <ErrorMessage
          message={t(serverError)}
          closeError={() => {
            setServerError(null);
            setSearchParams("");
          }}
        />
      )}
    </>
  );
};