482 lines
15 KiB
TypeScript
482 lines
15 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import {
|
|
Routes,
|
|
Route,
|
|
Navigate,
|
|
Outlet,
|
|
useLocation,
|
|
useNavigate,
|
|
} from "react-router-dom";
|
|
import { useAuth } from "@/auth";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import { actions, selectors } from "@/store";
|
|
import routes, {
|
|
hasNavigation,
|
|
getRouteBy,
|
|
hasNoFooter,
|
|
hasNoHeader,
|
|
hasNavbarFooter,
|
|
hasFullDataModal,
|
|
} from "@/routes";
|
|
import BirthdayPage from "../BirthdayPage";
|
|
import BirthtimePage from "../BirthtimePage";
|
|
import CreateProfilePage from "../CreateProfilePage";
|
|
import EmailEnterPage from "../EmailEnterPage";
|
|
import SubscriptionPage from "../SubscriptionPage";
|
|
import PaymentPage from "../PaymentPage";
|
|
import WallpaperPage from "../WallpaperPage";
|
|
import StaticPage from "../StaticPage";
|
|
import NotFoundPage from "../NotFoundPage";
|
|
import Header from "../Header";
|
|
import Navbar from "../Navbar";
|
|
import Footer from "../Footer";
|
|
import "./styles.css";
|
|
import DidYouKnowPage from "../DidYouKnowPage";
|
|
import FreePeriodInfoPage from "../FreePeriodInfoPage";
|
|
import AttentionPage from "../AttentionPage";
|
|
import FeedbackPage from "../FeedbackPage";
|
|
import CompatibilityPage from "../Compatibility";
|
|
import BreathPage from "../BreathPage";
|
|
import PriceListPage from "../PriceListPage";
|
|
import CompatResultPage from "../CompatResultPage";
|
|
import HomePage from "../HomePage";
|
|
import UserCallbacksPage from "../UserCallbacksPage";
|
|
import NavbarFooter, { INavbarHomeItems } from "../NavbarFooter";
|
|
import { EPathsFromHome } from "@/store/siteConfig";
|
|
import parseAPNG, { APNG } from "apng-js";
|
|
import { useApi, useApiCall } from "@/api";
|
|
import { Asset } from "@/api/resources/Assets";
|
|
import PaymentResultPage from "../PaymentPage/results";
|
|
import PaymentSuccessPage from "../PaymentPage/results/SuccessPage";
|
|
import PaymentFailPage from "../PaymentPage/results/ErrorPage";
|
|
import { StripePage } from "../StripePage";
|
|
import AuthPage from "../AuthPage";
|
|
import AuthResultPage from "../AuthResultPage";
|
|
import MagicBallPage from "../pages/MagicBall";
|
|
import BestiesHoroscopeResult from "../pages/BestiesHoroscopeResult";
|
|
import PredictionMoonResult from "../pages/PredictionMoonResult";
|
|
import MyHoroscopeResult from "../pages/MyHoroscopeResult";
|
|
import ThermalResult from "../pages/ThermalResult";
|
|
import MoonPhaseTrackerResult from "../pages/MoonPhaseTrackerResult";
|
|
import EnergyVampirismResult from "../pages/EnergyVampirismResult";
|
|
import NameHoroscopeResult from "../pages/NameHoroscopeResult";
|
|
import GenderPage from "../pages/Gender";
|
|
import QuestionnairePage from "../pages/Questionnaire";
|
|
import GoalSetupPage from "../pages/GoalSetup";
|
|
import HyperPersonalizedAstrologyPage from "../pages/HyperPersonalizedAstrologyPage";
|
|
import NoBirthtimePage from "../pages/NoBirthtime";
|
|
import LoadingInRelationshipPage from "../pages/LoadingInRelationship";
|
|
import WorksForUsPage from "../pages/WorksForUs";
|
|
import RelationshipAlmostTherePage from "../pages/RelationshipAlmostThere";
|
|
import Modal from "../Modal";
|
|
import FullDataModal from "../FullDataModal";
|
|
|
|
function App(): JSX.Element {
|
|
const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState<boolean>(false);
|
|
const [leoApng, setLeoApng] = useState<Error | APNG>(Error);
|
|
const [padLockApng, setPadLockApng] = useState<Error | APNG>(Error);
|
|
const navigate = useNavigate();
|
|
const api = useApi();
|
|
const dispatch = useDispatch();
|
|
const { token, user } = useAuth();
|
|
|
|
const closeSpecialOfferAttention = () => {
|
|
setIsSpecialOfferOpen(false);
|
|
navigate(routes.client.auth());
|
|
};
|
|
|
|
const assetsData = useCallback(async () => {
|
|
const { assets } = await api.getAssets({
|
|
category: String("au"),
|
|
});
|
|
return assets;
|
|
}, [api]);
|
|
|
|
const { data } = useApiCall<Asset[]>(assetsData);
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
if (!token.length || !user) return;
|
|
const {
|
|
user: { has_subscription },
|
|
} = await api.getSubscriptionStatus({
|
|
token,
|
|
});
|
|
|
|
if (has_subscription && user) {
|
|
return dispatch(actions.status.update("subscribed"));
|
|
}
|
|
if (!has_subscription && user) {
|
|
return dispatch(actions.status.update("unsubscribed"));
|
|
}
|
|
if (!user) {
|
|
return dispatch(actions.status.update("lead"));
|
|
}
|
|
})();
|
|
}, [dispatch, api, token, user]);
|
|
|
|
useEffect(() => {
|
|
async function getApng() {
|
|
if (!data) return;
|
|
const response = await fetch(
|
|
data.find((item) => item.key === "au.apng.leo")?.url || ""
|
|
);
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
setLeoApng(parseAPNG(arrayBuffer));
|
|
}
|
|
getApng();
|
|
}, [data]);
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
const response = await fetch("/padlock_icon_animation_closing.png");
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
setPadLockApng(parseAPNG(arrayBuffer));
|
|
})();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!user) return;
|
|
dispatch(actions.form.addEmail(user.email));
|
|
}, []);
|
|
|
|
return (
|
|
<Routes>
|
|
<Route element={<Layout setIsSpecialOfferOpen={setIsSpecialOfferOpen} />}>
|
|
{/* Test Routes Start */}
|
|
<Route path={routes.client.notFound()} element={<NotFoundPage />} />
|
|
<Route path={routes.client.gender()} element={<GenderPage />} />
|
|
<Route
|
|
path={routes.client.questionnaire()}
|
|
element={<QuestionnairePage />}
|
|
>
|
|
<Route path=":stepId" element={<QuestionnairePage />}>
|
|
<Route path=":question" element={<QuestionnairePage />} />
|
|
</Route>
|
|
</Route>
|
|
<Route path={routes.client.goalSetup()} element={<GoalSetupPage />} />
|
|
<Route
|
|
path={routes.client.hyperPersonalizedAstrology()}
|
|
element={<HyperPersonalizedAstrologyPage />}
|
|
/>
|
|
<Route path={routes.client.noTime()} element={<NoBirthtimePage />} />
|
|
<Route
|
|
path={routes.client.loadingInRelationship()}
|
|
element={<LoadingInRelationshipPage />}
|
|
/>
|
|
<Route path={routes.client.worksForUs()} element={<WorksForUsPage />} />
|
|
<Route
|
|
path={routes.client.relationshipAlmostThere()}
|
|
element={<RelationshipAlmostTherePage />}
|
|
/>
|
|
{/* Test Routes End */}
|
|
|
|
<Route
|
|
path={routes.client.paymentResult()}
|
|
element={<PaymentResultPage />}
|
|
/>
|
|
<Route
|
|
path={routes.client.paymentSuccess()}
|
|
element={<PaymentSuccessPage />}
|
|
/>
|
|
<Route
|
|
path={routes.client.paymentFail()}
|
|
element={<PaymentFailPage />}
|
|
/>
|
|
<Route element={<AuthorizedUserOutlet />}>
|
|
<Route path={routes.client.root()} element={<MainPage />} />
|
|
<Route path={routes.client.birthday()} element={<BirthdayPage />} />
|
|
<Route
|
|
path={routes.client.didYouKnow()}
|
|
element={<DidYouKnowPage />}
|
|
/>
|
|
<Route
|
|
path={routes.client.freePeriodInfo()}
|
|
element={<FreePeriodInfoPage />}
|
|
/>
|
|
<Route
|
|
path={routes.client.attention()}
|
|
element={
|
|
<AttentionPage
|
|
isOpenModal={isSpecialOfferOpen}
|
|
onCloseSpecialOffer={closeSpecialOfferAttention}
|
|
/>
|
|
}
|
|
/>
|
|
<Route path={routes.client.feedback()} element={<FeedbackPage />} />
|
|
<Route path={routes.client.birthtime()} element={<BirthtimePage />} />
|
|
<Route path={routes.client.createProfile()} element={<SkipStep />} />
|
|
<Route
|
|
path={routes.client.emailEnter()}
|
|
element={<EmailEnterPage />}
|
|
/>
|
|
<Route
|
|
path={routes.client.auth()}
|
|
element={<AuthPage padLockApng={padLockApng} />}
|
|
/>
|
|
<Route
|
|
path={routes.client.authResult()}
|
|
element={<AuthResultPage />}
|
|
/>
|
|
<Route path={routes.client.static()} element={<StaticPage />} />
|
|
<Route path={routes.client.priceList()} element={<PriceListPage />} />
|
|
{/* <Route
|
|
path={routes.client.wallpaper()}
|
|
element={<ProtectWallpaperPage />}
|
|
/> */}
|
|
</Route>
|
|
<Route element={<AuthorizedUserOutlet />}>
|
|
<Route
|
|
path={routes.client.subscription()}
|
|
element={<SubscriptionPage />}
|
|
>
|
|
<Route path=":subPlan" element={<SubscriptionPage />} />
|
|
</Route>
|
|
</Route>
|
|
{/* <Route element={<PrivateOutlet />}> */}
|
|
<Route element={<AuthorizedUserOutlet />}>
|
|
{/* <Route
|
|
path={routes.client.subscription()}
|
|
element={<SubscriptionPage />}
|
|
/> */}
|
|
<Route
|
|
path={routes.client.paymentMethod()}
|
|
element={<PaymentPage />}
|
|
/>
|
|
<Route
|
|
path={routes.client.paymentStripe()}
|
|
element={<StripePage />}
|
|
/>
|
|
</Route>
|
|
{/* <Route element={<PrivateSubscriptionOutlet />}> */}
|
|
<Route path={routes.client.home()} element={<HomePage />} />
|
|
<Route
|
|
path={routes.client.compatibility()}
|
|
element={<CompatibilityPage />}
|
|
/>
|
|
<Route
|
|
path={routes.client.compatibilityResult()}
|
|
element={<CompatResultPage />}
|
|
/>
|
|
<Route
|
|
path={routes.client.breath()}
|
|
element={<BreathPage leoApng={leoApng} />}
|
|
/>
|
|
<Route
|
|
path={routes.client.breathResult()}
|
|
element={<UserCallbacksPage />}
|
|
/>
|
|
<Route path={routes.client.wallpaper()} element={<WallpaperPage />} />
|
|
<Route path={routes.client.magicBall()} element={<MagicBallPage />} />
|
|
<Route
|
|
path={routes.client.horoscopeBestiesResult()}
|
|
element={<BestiesHoroscopeResult />}
|
|
/>
|
|
<Route
|
|
path={routes.client.predictionMoonResult()}
|
|
element={<PredictionMoonResult />}
|
|
/>
|
|
<Route
|
|
path={routes.client.myHoroscopeResult()}
|
|
element={<MyHoroscopeResult />}
|
|
/>
|
|
<Route
|
|
path={routes.client.thermalResult()}
|
|
element={<ThermalResult />}
|
|
/>
|
|
<Route
|
|
path={routes.client.moonPhaseTracker()}
|
|
element={<MoonPhaseTrackerResult />}
|
|
/>
|
|
<Route
|
|
path={routes.client.energyVampirismResult()}
|
|
element={<EnergyVampirismResult />}
|
|
/>
|
|
<Route
|
|
path={routes.client.nameHoroscopeResult()}
|
|
element={<NameHoroscopeResult />}
|
|
/>
|
|
{/* </Route> */}
|
|
{/* </Route> */}
|
|
<Route path="*" element={<NotFoundPage />} />
|
|
</Route>
|
|
</Routes>
|
|
);
|
|
}
|
|
|
|
interface LayoutProps {
|
|
setIsSpecialOfferOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
}
|
|
|
|
function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element {
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
const dispatch = useDispatch();
|
|
const showNavbar = hasNavigation(location.pathname);
|
|
const showFooter = hasNoFooter(location.pathname);
|
|
const showHeader = hasNoHeader(location.pathname);
|
|
const isRouteFullDataModal = hasFullDataModal(location.pathname);
|
|
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
|
|
const changeIsSpecialOfferOpen = () => setIsSpecialOfferOpen(true);
|
|
const homeConfig = useSelector(selectors.selectHome);
|
|
const showNavbarFooter = homeConfig.isShowNavbar;
|
|
|
|
const birthdate = useSelector(selectors.selectBirthdate);
|
|
const dataItems = useMemo(() => [birthdate], [birthdate]);
|
|
const [isShowFullDataModal, setIsShowFullDataModal] =
|
|
useState<boolean>(false);
|
|
|
|
useEffect(() => {
|
|
setIsShowFullDataModal(getIsShowFullDataModal(dataItems));
|
|
}, [dataItems]);
|
|
|
|
const onCloseFullDataModal = (_birthDate: string) => {
|
|
console.log("onCloseFullDataModal", _birthDate);
|
|
dispatch(actions.form.addDate(_birthDate));
|
|
setIsShowFullDataModal(getIsShowFullDataModal(dataItems));
|
|
};
|
|
|
|
const handleCompatibility = () => {
|
|
dispatch(
|
|
actions.siteConfig.update({
|
|
home: {
|
|
pathFromHome: EPathsFromHome.navbar,
|
|
isShowNavbar: showNavbarFooter,
|
|
},
|
|
})
|
|
);
|
|
navigate(routes.client.compatibility());
|
|
};
|
|
const handleBreath = () => {
|
|
dispatch(
|
|
actions.siteConfig.update({
|
|
home: {
|
|
pathFromHome: EPathsFromHome.navbar,
|
|
isShowNavbar: showNavbarFooter,
|
|
},
|
|
})
|
|
);
|
|
navigate(routes.client.breath());
|
|
};
|
|
|
|
const navbarItems: INavbarHomeItems[] = [
|
|
{
|
|
title: "Breathing",
|
|
path: routes.client.breath(),
|
|
paths: [routes.client.breath(), routes.client.breathResult()],
|
|
image: "Breath.svg",
|
|
onClick: handleBreath,
|
|
},
|
|
{
|
|
title: "Aura",
|
|
path: routes.client.home(),
|
|
paths: [routes.client.home()],
|
|
image: "Aura.svg",
|
|
active: true,
|
|
onClick: () => null,
|
|
},
|
|
{
|
|
title: "Compatibility",
|
|
path: routes.client.compatibility(),
|
|
paths: [
|
|
routes.client.compatibility(),
|
|
routes.client.compatibilityResult(),
|
|
],
|
|
image: "Compatibility.svg",
|
|
onClick: handleCompatibility,
|
|
},
|
|
{
|
|
title: "My Moon",
|
|
path: routes.client.wallpaper(),
|
|
paths: [routes.client.wallpaper()],
|
|
image: "moon.svg",
|
|
onClick: () => null,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="container">
|
|
{showHeader ? (
|
|
<Header
|
|
openMenu={() => setIsMenuOpen(true)}
|
|
clickCross={changeIsSpecialOfferOpen}
|
|
/>
|
|
) : null}
|
|
{isRouteFullDataModal && (
|
|
<Modal open={isShowFullDataModal} isCloseButtonVisible={false}>
|
|
<FullDataModal onClose={onCloseFullDataModal} />
|
|
</Modal>
|
|
)}
|
|
<main className="content">
|
|
<Outlet />
|
|
</main>
|
|
{showFooter ? <Footer color={showNavbar ? "black" : "white"} /> : null}
|
|
{showNavbar ? (
|
|
<Navbar isOpen={isMenuOpen} closeMenu={() => setIsMenuOpen(false)} />
|
|
) : null}
|
|
{showNavbarFooter && hasNavbarFooter(location.pathname) ? (
|
|
<NavbarFooter items={navbarItems} />
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function AuthorizedUserOutlet(): JSX.Element {
|
|
const status = useSelector(selectors.selectStatus);
|
|
const { user } = useAuth();
|
|
return user && status === "subscribed" ? (
|
|
<Navigate to={routes.client.home()} replace={true} />
|
|
) : (
|
|
<Outlet />
|
|
);
|
|
}
|
|
|
|
// function PrivateOutlet(): JSX.Element {
|
|
// const { user } = useAuth();
|
|
// return user ? (
|
|
// <Outlet />
|
|
// ) : (
|
|
// <Navigate to={routes.client.root()} replace={true} />
|
|
// );
|
|
// }
|
|
|
|
// function PrivateSubscriptionOutlet(): JSX.Element {
|
|
// // const isProduction = import.meta.env.MODE === "production";
|
|
// const isProduction = false;
|
|
// const status = useSelector(selectors.selectStatus);
|
|
// return status === "subscribed" || !isProduction ? (
|
|
// <Outlet />
|
|
// ) : (
|
|
// <Navigate to={getRouteBy(status)} replace={true} />
|
|
// );
|
|
// }
|
|
|
|
function getIsShowFullDataModal(dataItems: Array<unknown> = []): boolean {
|
|
let hasNoDataItem = false;
|
|
|
|
for (const item of dataItems) {
|
|
if (!item) {
|
|
hasNoDataItem = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hasNoDataItem;
|
|
}
|
|
|
|
function SkipStep(): JSX.Element {
|
|
const { user } = useAuth();
|
|
return user ? (
|
|
<Navigate to={routes.client.emailEnter()} replace={true} />
|
|
) : (
|
|
<CreateProfilePage />
|
|
);
|
|
}
|
|
|
|
function MainPage(): JSX.Element {
|
|
const status = useSelector(selectors.selectStatus);
|
|
return <Navigate to={getRouteBy(status)} replace={true} />;
|
|
}
|
|
|
|
export default App;
|