w-aura/src/components/App/index.tsx

365 lines
11 KiB
TypeScript

import { useCallback, useEffect, 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,
} 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";
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} />}>
<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={<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>
</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 [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
const changeIsSpecialOfferOpen = () => setIsSpecialOfferOpen(true);
const homeConfig = useSelector(selectors.selectHome);
const showNavbarFooter = homeConfig.isShowNavbar;
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}
<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 status = useSelector(selectors.selectStatus);
return status === "subscribed" ? (
<Outlet />
) : (
<Navigate to={getRouteBy(status)} replace={true} />
);
}
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;