EscolaLMS/Front

View on GitHub
src/components/Courses/SingleCoursesTwo/CoursesDetailsSidebar/Buttons/index.tsx

Summary

Maintainability
D
2 days
Test Coverage
import React, {
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";
import styled from "styled-components";
import { EscolaLMSContext } from "@escolalms/sdk/lib/react";
import isPast from "date-fns/isPast";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { Capacitor } from "@capacitor/core";
import { Purchases } from "@revenuecat/purchases-capacitor";
import { VITE_APP_ANDROID_APIKEY, VITE_APP_IOS_APIKEY } from "@/config/index";
import { Button } from "@escolalms/components/lib/components/atoms/Button/Button";
import { Text } from "@escolalms/components/lib/components/atoms/Typography/Text";
import { API } from "@escolalms/sdk/lib";
import { isMobilePlatform, userIsCourseAuthor } from "@/utils/index";
import routeRoutes from "@/components/Routes/routes";
import { Modal } from "@escolalms/components/lib/components/atoms/Modal/Modal";
import ProductModal from "@/components/Courses/SingleCoursesTwo/CoursesDetailsSidebar/ProductModal";
import useSubscriptions from "@/hooks/useSubscriptions";
import {
  findProductByIdentifier,
  getRevenuecatId,
  revenuecatErrorHandler,
} from "@/utils/payment";
import { CapacitorPaymentError } from "@/types/index";

interface CourseAccessButtonProps {
  course: API.Course;
  onRequestAccess: () => void;
  setModalVisible: (visible: boolean) => void;
}

const StyledButton = styled(Button)`
  display: block;
  margin-bottom: 10px;
`;

const CourseAccessButton: React.FC<CourseAccessButtonProps> = ({
  course,
  onRequestAccess,
  setModalVisible,
}) => {
  const { t } = useTranslation();
  const { push } = useHistory();
  const { courseAccess, fetchCourseAccess, user, fetchCourse } =
    useContext(EscolaLMSContext);
  const { attachProduct, getActiveSubscription } = useSubscriptions();

  useEffect(() => {
    (async function () {
      const id = user?.value?.id;

      if (id && isMobilePlatform) {
        if (Capacitor.getPlatform() === "ios") {
          await Purchases.configure({
            apiKey: VITE_APP_IOS_APIKEY,
            appUserID: `${id}`,
          });
        } else if (Capacitor.getPlatform() === "android") {
          await Purchases.configure({
            apiKey: VITE_APP_ANDROID_APIKEY,
            appUserID: `${id}`,
          });
        }
      }
    })();
  }, [user?.value?.id]);

  const buyOnMobile = useCallback(async () => {
    const id = getRevenuecatId(course);
    const offerings = await Purchases.getOfferings();
    const packages = offerings?.current?.availablePackages || [];

    const product = findProductByIdentifier(packages, id);

    if (product) {
      try {
        await Purchases.purchaseStoreProduct({
          product: product,
        });
        // Refresh course access after successful purchase
        // We need timeout for android phones (3s is enough)
        setTimeout(() => {
          fetchCourse(Number(course.id));
          fetchCourseAccess({
            course_id: Number(course.id),
            current_page: 1,
            per_page: 1,
          });
        }, 3000);
      } catch (error) {
        revenuecatErrorHandler(error as CapacitorPaymentError);
      }
    }
  }, [course, fetchCourse, fetchCourseAccess]);

  const currentCourseAccess = useMemo(
    () =>
      courseAccess.list?.data?.find(
        (courseAccessItem) => courseAccessItem?.course?.id === course.id
      ),
    [courseAccess.list?.data, course.id]
  );

  const handleBuyButtonClick = useCallback(() => {
    setModalVisible(true);
  }, [setModalVisible]);

  const handleAttachProduct = useCallback(() => {
    attachProduct(course.id, "EscolaLms\\Courses\\Models\\Course").then(() => [
      push(`/course/${course.id}`),
      fetchCourseAccess({
        course_id: Number(course.id),
        current_page: 1,
        per_page: 1,
      }),
    ]);
  }, [attachProduct, course.id, push, fetchCourseAccess]);

  const BuyButton = useMemo(
    () => (
      <Button
        mode="secondary"
        onClick={() => {
          if (isMobilePlatform) {
            buyOnMobile();
            return;
          }
          handleBuyButtonClick();
        }}
      >
        {t("Buy Course")}
      </Button>
    ),
    [t, handleBuyButtonClick, buyOnMobile]
  );

  if (getActiveSubscription?.id) {
    return (
      <Button onClick={() => handleAttachProduct()} mode="secondary">
        {t("Go to the course")}
      </Button>
    );
  }
  if (!currentCourseAccess) {
    return (
      <>
        <StyledButton mode="secondary" onClick={onRequestAccess}>
          {t("CourseAccess.RequestAccess")}
        </StyledButton>
        {BuyButton}
      </>
    );
  }

  if (currentCourseAccess.status !== "approved") {
    return (
      <>
        <StyledButton mode="secondary" disabled>
          {t("CourseAccess.Pending")}
        </StyledButton>
        {BuyButton}
      </>
    );
  }

  if (currentCourseAccess.status === "approved") {
    return (
      <Button onClick={() => push(`/course/${course.id}`)} mode="secondary">
        {t("Go to the course")}
      </Button>
    );
  }

  return BuyButton;
};

type Props = {
  course: API.Course;
  userOwnThisCourse: boolean | undefined;
  onRequestAccess: () => void;
};

const CourseDetailsSidebarButtons: React.FC<Props> = ({
  course,
  userOwnThisCourse,
  onRequestAccess,
}) => {
  const { cart, user, fetchCourseAccess } = useContext(EscolaLMSContext);
  const { t } = useTranslation();
  const { push } = useHistory();
  const [modalVisible, setModalVisible] = useState(false);
  const courseInCart = useMemo(() => {
    return cart?.value?.items.some(
      (item) => Number(item.product_id) === Number(course.product?.id)
    );
  }, [course.product?.id, cart]);

  useEffect(() => {
    fetchCourseAccess({
      course_id: Number(course.id),
      current_page: 1,
      per_page: 1,
    });
  }, [course.id, fetchCourseAccess]);

  if (userIsCourseAuthor(Number(user.value?.id), course)) {
    return (
      <Button onClick={() => push(`/course/${course.id}`)} mode="secondary">
        {t("Go to the course")}
      </Button>
    );
  }

  if (isPast(new Date(course.active_to || ""))) {
    return <Text>{t("CoursePage.IsFinished")}</Text>;
  }
  if (courseInCart) {
    return (
      <>
        <Button mode="secondary" onClick={() => push(routeRoutes.cart)}>
          {t("CoursePage.GoToCheckout")}
        </Button>
      </>
    );
  }
  if (userOwnThisCourse && isPast(new Date(course.active_from || ""))) {
    return (
      <Button onClick={() => push(`/course/${course.id}`)} mode="secondary">
        {t("Go to the course")}
      </Button>
    );
  }
  if (userOwnThisCourse) {
    return <Text>{t("CoursePage.NotStarted")}</Text>;
  }
  if (user.value && course.product) {
    return (
      <>
        <CourseAccessButton
          course={course}
          onRequestAccess={onRequestAccess}
          setModalVisible={setModalVisible}
        />

        <Modal
          onClose={() => setModalVisible(false)}
          visible={modalVisible}
          animation="zoom"
          maskAnimation="fade"
          destroyOnClose
          width={800}
        >
          <ProductModal course={course} />
        </Modal>
      </>
    );
  }
  if (!course.product) {
    return <Text>{t("CoursePage.UnavailableCourse")}</Text>;
  }
  return (
    <Button
      onClick={() => push(`/login?referrer=/courses/${course.id}`)}
      mode="secondary"
    >
      {t("Buy Course")}
    </Button>
  );
};

export default CourseDetailsSidebarButtons;