teamdigitale/italia-app

View on GitHub
ts/features/fci/screens/valid/FciDocumentsScreen.tsx

Summary

Maintainability
D
2 days
Test Coverage
import * as React from "react";
import Pdf from "react-native-pdf";
import { pipe } from "fp-ts/lib/function";
import * as RA from "fp-ts/lib/ReadonlyArray";
import * as O from "fp-ts/lib/Option";
import * as S from "fp-ts/lib/string";
import { StyleSheet, View } from "react-native";
import {
  RouteProp,
  StackActions,
  useIsFocused,
  useNavigation,
  useRoute
} from "@react-navigation/native";
import {
  BlockButtonProps,
  ButtonSolidProps,
  FooterWithButtons,
  IOColors,
  IOStyles
} from "@pagopa/io-app-design-system";
import I18n from "../../../../i18n";
import DocumentsNavigationBar from "../../components/DocumentsNavigationBar";
import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp";
import { useFciAbortSignatureFlow } from "../../hooks/useFciAbortSignatureFlow";
import { fciSignatureDetailDocumentsSelector } from "../../store/reducers/fciSignatureRequest";
import { FCI_ROUTES } from "../../navigation/routes";
import { TypeEnum as ClauseType } from "../../../../../definitions/fci/Clause";
import { FciParamsList } from "../../navigation/params";
import { DocumentToSign } from "../../../../../definitions/fci/DocumentToSign";
import {
  fciClearStateRequest,
  fciDownloadPreview,
  fciUpdateDocumentSignaturesRequest
} from "../../store/actions";
import { fciDocumentSignaturesSelector } from "../../store/reducers/fciDocumentSignatures";
import { useIODispatch, useIOSelector } from "../../../../store/hooks";
import { fciDownloadPathSelector } from "../../store/reducers/fciDownloadPreview";
import { trackFciDocOpeningSuccess, trackFciSigningDoc } from "../../analytics";
import {
  getOptionalSignatureFields,
  getRequiredSignatureFields,
  getSignatureFieldsLength
} from "../../utils/signatureFields";
import { useFciNoSignatureFields } from "../../hooks/useFciNoSignatureFields";
import { fciEnvironmentSelector } from "../../store/reducers/fciEnvironment";
import LoadingComponent from "../../components/LoadingComponent";
import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel";

const styles = StyleSheet.create({
  pdf: {
    flex: 1,
    backgroundColor: IOColors.bluegrey
  }
});

export type FciDocumentsScreenNavigationParams = Readonly<{
  currentDoc: number;
}>;

const FciDocumentsScreen = () => {
  const pdfRef = React.useRef<Pdf>(null);
  const [totalPages, setTotalPages] = React.useState(0);
  const [currentPage, setCurrentPage] = React.useState(1);
  const route = useRoute<RouteProp<FciParamsList, "FCI_DOCUMENTS">>();
  const currentDoc = route.params.currentDoc ?? 0;
  const documents = useIOSelector(fciSignatureDetailDocumentsSelector);
  const downloadPath = useIOSelector(fciDownloadPathSelector);
  const fciEnvironment = useIOSelector(fciEnvironmentSelector);
  const navigation = useNavigation();
  const documentSignaturesSelector = useIOSelector(
    fciDocumentSignaturesSelector
  );
  const dispatch = useIODispatch();
  const isFocused = useIsFocused();

  React.useEffect(() => {
    if (documents.length !== 0 && isFocused) {
      dispatch(fciDownloadPreview.request({ url: documents[currentDoc].url }));
    }
    // if the user hasn't checked any signauture field,
    // we need to initialize the documentSignatures state
    if (RA.isEmpty(documentSignaturesSelector)) {
      pipe(
        documents,
        RA.map(d => {
          const docSignature = {
            document_id: d.id,
            signature_fields: d.metadata.signature_fields.filter(
              s => s.clause.type === ClauseType.REQUIRED
            )
          } as DocumentToSign;
          dispatch(fciUpdateDocumentSignaturesRequest(docSignature));
        })
      );
    }
  }, [dispatch, documentSignaturesSelector, documents, currentDoc, isFocused]);

  React.useEffect(() => {
    // with a document opened, we can track the opening success event
    if (documents[currentDoc] && isFocused) {
      trackFciDocOpeningSuccess(
        currentDoc + 1,
        getRequiredSignatureFields(
          documents[currentDoc]?.metadata.signature_fields
        ).length,
        getOptionalSignatureFields(
          documents[currentDoc]?.metadata.signature_fields
        ).length,
        fciEnvironment
      );
    }
  }, [currentDoc, documents, isFocused, fciEnvironment]);

  const { present, bottomSheet: fciAbortSignature } =
    useFciAbortSignatureFlow();

  const {
    present: showNoSignatureFieldsBs,
    bottomSheet: fciNoSignatureFields
  } = useFciNoSignatureFields({ currentDoc });

  const onContinuePress = () => {
    if (getSignatureFieldsLength(documents[currentDoc]) > 0) {
      trackFciSigningDoc(fciEnvironment);
      navigation.dispatch(
        StackActions.push(FCI_ROUTES.SIGNATURE_FIELDS, {
          documentId: documents[currentDoc].id,
          currentDoc
        })
      );
    } else {
      showNoSignatureFieldsBs();
    }
  };

  const onCancelPress = () => present();

  const cancelButtonProps: ButtonSolidProps = {
    onPress: onCancelPress,
    label: I18n.t("features.fci.documents.footer.cancel"),
    accessibilityLabel: I18n.t("features.fci.documents.footer.cancel")
  };

  const continueButtonProps: ButtonSolidProps = {
    onPress: onContinuePress,
    label: I18n.t("features.fci.documents.footer.continue"),
    accessibilityLabel: I18n.t("features.fci.documents.footer.continue")
  };

  const keepReadingButtonProps: ButtonSolidProps = {
    onPress: () => pointToPage(totalPages),
    label: I18n.t("global.buttons.continue"),
    accessibilityLabel: I18n.t("global.buttons.continue")
  };

  const secondaryButtonProps: BlockButtonProps = {
    type: currentPage < totalPages ? "Outline" : "Solid",
    buttonProps:
      currentPage < totalPages ? keepReadingButtonProps : continueButtonProps
  };

  const pointToPage = (page: number) =>
    pipe(
      pdfRef.current,
      O.fromNullable,
      O.map(_ => _.setPage(page))
    );

  const renderPager = () => (
    <Pdf
      ref={pdfRef}
      source={{
        uri: `${downloadPath}`
      }}
      onLoadComplete={(numberOfPages, _) => {
        setTotalPages(numberOfPages);
      }}
      onPageChanged={(page, _) => {
        setCurrentPage(page);
      }}
      enablePaging
      style={styles.pdf}
    />
  );

  const onPrevious = () => {
    pipe(
      currentPage,
      O.fromNullable,
      O.chain(page => (page > 1 ? O.some(page - 1) : O.none)),
      O.map(page => {
        setCurrentPage(page);
        pointToPage(page);
      })
    );
  };

  const onNext = () => {
    pipe(
      currentPage,
      O.fromNullable,
      O.chain(page => (page < totalPages ? O.some(page + 1) : O.none)),
      O.map(page => {
        setCurrentPage(page);
        pointToPage(page);
      })
    );
  };

  useHeaderSecondLevel({
    title: I18n.t("features.fci.title"),
    supportRequest: true,
    contextualHelp: emptyContextualHelp,
    goBack: () => {
      if (currentDoc <= 0) {
        dispatch(fciClearStateRequest());
      }
      navigation.goBack();
    }
  });

  if (S.isEmpty(downloadPath)) {
    return <LoadingComponent />;
  }

  return (
    <>
      <DocumentsNavigationBar
        indicatorPosition={"right"}
        titleLeft={I18n.t("features.fci.documentsBar.titleLeft", {
          currentDoc: currentDoc + 1,
          totalDocs: documents.length
        })}
        titleRight={I18n.t("features.fci.documentsBar.titleRight", {
          currentPage,
          totalPages
        })}
        iconLeftDisabled={currentPage === 1}
        iconRightDisabled={currentPage === totalPages}
        onPrevious={onPrevious}
        onNext={onNext}
        disabled={false}
        testID={"FciDocumentsNavBarTestID"}
      />
      <View style={IOStyles.flex} testID={"FciDocumentsScreenTestID"}>
        {documents.length > 0 && (
          <>
            {renderPager()}
            <FooterWithButtons
              type="TwoButtonsInlineThird"
              secondary={secondaryButtonProps}
              primary={{ type: "Outline", buttonProps: cancelButtonProps }}
            />
          </>
        )}
      </View>
      {fciAbortSignature}
      {fciNoSignatureFields}
    </>
  );
};
export default FciDocumentsScreen;