174 lines
5.2 KiB
TypeScript
174 lines
5.2 KiB
TypeScript
import styles from "./styles.module.css";
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { UserCallbacks, useApi, useApiCall } from "@/api";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import { actions, selectors } from "@/store";
|
|
import { useTranslation } from "react-i18next";
|
|
import FullScreenModal from "../FullScreenModal";
|
|
import StartBreathModalChild from "../StartBreathModalChild";
|
|
import Title from "../Title";
|
|
import { useNavigate } from "react-router-dom";
|
|
import routes from "@/routes";
|
|
import { APNG } from "apng-js";
|
|
import Player from "apng-js/types/library/player";
|
|
|
|
let apngPlayer: Player | null = null;
|
|
|
|
interface BreathPageProps {
|
|
leoApng: Error | APNG;
|
|
}
|
|
|
|
function BreathPage({ leoApng }: BreathPageProps): JSX.Element {
|
|
const { t } = useTranslation();
|
|
const [isOpenModal, setIsOpenModal] = useState<boolean>(true);
|
|
const [isShowPreview, setIsShowPreview] = useState<boolean>(true);
|
|
const leoCanvasRef = useRef<HTMLCanvasElement>(null);
|
|
const pageRef = useRef<HTMLElement>(null);
|
|
const api = useApi();
|
|
const dispatch = useDispatch();
|
|
const navigate = useNavigate();
|
|
const homeConfig = useSelector(selectors.selectHome);
|
|
const showNavbarFooter = homeConfig.isShowNavbar;
|
|
const queryTimeOutRef = useRef<NodeJS.Timeout>();
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (queryTimeOutRef.current) {
|
|
clearTimeout(queryTimeOutRef.current);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (isOpenModal) return;
|
|
const previewTimeOut = setTimeout(() => {
|
|
setIsShowPreview(false);
|
|
apngPlayer?.play();
|
|
}, 10_000);
|
|
|
|
const navigateTimeOut = setTimeout(() => {
|
|
navigate(routes.client.breathResult());
|
|
}, 50_000);
|
|
|
|
return () => {
|
|
clearTimeout(navigateTimeOut);
|
|
clearTimeout(previewTimeOut);
|
|
};
|
|
}, [navigate, isOpenModal]);
|
|
|
|
useEffect(() => {
|
|
async function getApngPlayer() {
|
|
const context = leoCanvasRef.current?.getContext("2d");
|
|
if (context && !(leoApng instanceof Error)) {
|
|
context.canvas.height = leoApng.height;
|
|
context.canvas.width = leoApng.width;
|
|
const _apngPlayer = await leoApng.getPlayer(context);
|
|
apngPlayer = _apngPlayer;
|
|
if (apngPlayer) {
|
|
apngPlayer.stop();
|
|
}
|
|
}
|
|
}
|
|
getApngPlayer();
|
|
}, [leoApng]);
|
|
|
|
const beginBreath = () => {
|
|
setIsOpenModal(false);
|
|
};
|
|
|
|
const token = useSelector(selectors.selectToken);
|
|
const createCallback = useCallback(async () => {
|
|
const data: UserCallbacks.PayloadPost = {
|
|
data: {
|
|
user_callback: {
|
|
kind: "breathing_end",
|
|
},
|
|
},
|
|
token,
|
|
};
|
|
const createCallbackRequest = await api.createUserCallbacks(data);
|
|
dispatch(actions.userCallbacks.update(createCallbackRequest.user_callback));
|
|
if (!createCallbackRequest.user_callback.is_complete) {
|
|
const getUserCallbacksRequest = async () => {
|
|
const getCallback = await api.getUserCallbacks({
|
|
id: createCallbackRequest.user_callback.id,
|
|
token,
|
|
});
|
|
if (!getCallback.user_callback.is_complete) {
|
|
queryTimeOutRef.current = setTimeout(getUserCallbacksRequest, 3000);
|
|
}
|
|
dispatch(
|
|
actions.userCallbacks.update({
|
|
description: getCallback.user_callback.description || "Loading...",
|
|
})
|
|
);
|
|
return getCallback.user_callback;
|
|
};
|
|
return await getUserCallbacksRequest();
|
|
}
|
|
|
|
return createCallbackRequest.user_callback;
|
|
}, [api, dispatch, token]);
|
|
|
|
useApiCall<UserCallbacks.IUserCallbacks>(createCallback);
|
|
|
|
const handleCross = () => {
|
|
navigate(routes.client.breathResult());
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<section
|
|
className={`${styles.page} page`}
|
|
ref={pageRef}
|
|
style={{ paddingBottom: showNavbarFooter ? "48px" : "16px" }}
|
|
>
|
|
{!showNavbarFooter && (
|
|
<div className={styles.cross} onClick={handleCross}></div>
|
|
)}
|
|
<canvas
|
|
style={{ display: isOpenModal ? "hidden" : "block" }}
|
|
className={`${styles["leo-apng"]} ${
|
|
!isOpenModal && isShowPreview ? styles.leo : ""
|
|
}`}
|
|
ref={leoCanvasRef}
|
|
></canvas>
|
|
{!isOpenModal && isShowPreview && (
|
|
<div className={styles.preview}>
|
|
<Title
|
|
variant="h2"
|
|
className={`${styles.text} ${styles["breath-relax"]}`}
|
|
>
|
|
{t("aura.breath_relax.text")}
|
|
</Title>
|
|
</div>
|
|
)}
|
|
<FullScreenModal isOpen={isOpenModal}>
|
|
<StartBreathModalChild
|
|
handleBegin={beginBreath}
|
|
isShowNavbar={showNavbarFooter}
|
|
/>
|
|
</FullScreenModal>
|
|
{!isOpenModal && !isShowPreview && (
|
|
<div className={styles["text-container"]}>
|
|
<Title
|
|
variant="h2"
|
|
className={`${styles.text} ${styles["breath-in"]}`}
|
|
>
|
|
{t("breathIn")}
|
|
</Title>
|
|
<Title
|
|
variant="h2"
|
|
className={`${styles.text} ${styles["breath-out"]}`}
|
|
>
|
|
{t("breathOut")}
|
|
</Title>
|
|
</div>
|
|
)}
|
|
</section>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default BreathPage;
|