feat: preload apng leo, page moons, logic of transitions between pages breath and compatibility, fix navbar footer, fix energy values
This commit is contained in:
parent
8b78a612fb
commit
864eb9ee4b
@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
Routes,
|
||||
Route,
|
||||
@ -42,16 +42,42 @@ 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";
|
||||
|
||||
function App(): JSX.Element {
|
||||
const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState<boolean>(false);
|
||||
const [leoApng, setLeoApng] = useState<Error | APNG>(Error);
|
||||
const navigate = useNavigate();
|
||||
const api = useApi();
|
||||
|
||||
const closeSpecialOfferAttention = () => {
|
||||
setIsSpecialOfferOpen(false);
|
||||
navigate(routes.client.emailEnter());
|
||||
};
|
||||
|
||||
const assetsData = useCallback(async () => {
|
||||
const { assets } = await api.getAssets({
|
||||
category: String("au"),
|
||||
});
|
||||
return assets;
|
||||
}, [api]);
|
||||
|
||||
const { data } = useApiCall<Asset[]>(assetsData);
|
||||
|
||||
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]);
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
<Route element={<Layout setIsSpecialOfferOpen={setIsSpecialOfferOpen} />}>
|
||||
@ -84,7 +110,10 @@ function App(): JSX.Element {
|
||||
path={routes.client.compatibilityResult()}
|
||||
element={<CompatResultPage />}
|
||||
/>
|
||||
<Route path={routes.client.breath()} element={<BreathPage />} />
|
||||
<Route
|
||||
path={routes.client.breath()}
|
||||
element={<BreathPage leoApng={leoApng} />}
|
||||
/>
|
||||
<Route path={routes.client.priceList()} element={<PriceListPage />} />
|
||||
<Route path={routes.client.home()} element={<HomePage />} />
|
||||
<Route
|
||||
@ -127,7 +156,7 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element {
|
||||
dispatch(
|
||||
actions.siteConfig.update({
|
||||
home: {
|
||||
pathFromHome: EPathsFromHome.compatibility,
|
||||
pathFromHome: EPathsFromHome.navbar,
|
||||
isShowNavbar: showNavbarFooter,
|
||||
},
|
||||
})
|
||||
@ -138,7 +167,7 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element {
|
||||
dispatch(
|
||||
actions.siteConfig.update({
|
||||
home: {
|
||||
pathFromHome: EPathsFromHome.breath,
|
||||
pathFromHome: EPathsFromHome.navbar,
|
||||
isShowNavbar: showNavbarFooter,
|
||||
},
|
||||
})
|
||||
@ -150,12 +179,14 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element {
|
||||
{
|
||||
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,
|
||||
@ -163,12 +194,17 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element {
|
||||
{
|
||||
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,
|
||||
},
|
||||
@ -221,7 +257,7 @@ function MainPage(): JSX.Element {
|
||||
|
||||
function ProtectWallpaperPage(): JSX.Element {
|
||||
const status = useSelector(selectors.selectStatus);
|
||||
return (<WallpaperPage />);
|
||||
return <WallpaperPage />;
|
||||
return status === "subscribed" ? (
|
||||
<WallpaperPage />
|
||||
) : (
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import styles from "./styles.module.css";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { UserCallbacks, useApi, useApiCall } from "@/api";
|
||||
import { Asset } from "@/api/resources/Assets";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@ -10,13 +9,17 @@ import StartBreathModalChild from "../StartBreathModalChild";
|
||||
import Title from "../Title";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import parseAPNG from "apng-js";
|
||||
import { APNG } from "apng-js";
|
||||
import Player from "apng-js/types/library/player";
|
||||
import { EPathsFromHome } from "@/store/siteConfig";
|
||||
|
||||
let apngPlayer: Player | null = null;
|
||||
|
||||
function BreathPage(): JSX.Element {
|
||||
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);
|
||||
@ -28,15 +31,6 @@ function BreathPage(): JSX.Element {
|
||||
const homeConfig = useSelector(selectors.selectHome);
|
||||
const showNavbarFooter = homeConfig.isShowNavbar;
|
||||
|
||||
const assetsData = useCallback(async () => {
|
||||
const { assets } = await api.getAssets({
|
||||
category: String("au"),
|
||||
});
|
||||
return assets;
|
||||
}, [api]);
|
||||
|
||||
const { data } = useApiCall<Asset[]>(assetsData);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpenModal) return;
|
||||
const previewTimeOut = setTimeout(() => {
|
||||
@ -55,26 +49,20 @@ function BreathPage(): JSX.Element {
|
||||
}, [navigate, isOpenModal]);
|
||||
|
||||
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();
|
||||
const apng = parseAPNG(arrayBuffer);
|
||||
async function getApngPlayer() {
|
||||
const context = leoCanvasRef.current?.getContext("2d");
|
||||
if (context && !(apng instanceof Error)) {
|
||||
context.canvas.height = apng.height;
|
||||
context.canvas.width = apng.width;
|
||||
const _apngPlayer = await apng.getPlayer(context);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
getApng();
|
||||
}, [data]);
|
||||
getApngPlayer();
|
||||
}, [leoApng]);
|
||||
|
||||
const beginBreath = () => {
|
||||
setIsOpenModal(false);
|
||||
@ -168,7 +156,10 @@ function BreathPage(): JSX.Element {
|
||||
</div>
|
||||
)}
|
||||
<FullScreenModal isOpen={isOpenModal}>
|
||||
<StartBreathModalChild handleBegin={beginBreath} isShowNavbar={showNavbarFooter} />
|
||||
<StartBreathModalChild
|
||||
handleBegin={beginBreath}
|
||||
isShowNavbar={showNavbarFooter}
|
||||
/>
|
||||
</FullScreenModal>
|
||||
{!isOpenModal && !isShowPreview && (
|
||||
<div className={styles["text-container"]}>
|
||||
|
||||
@ -25,6 +25,8 @@ function CompatResultPage(): JSX.Element {
|
||||
const showNavbarFooter = homeConfig.isShowNavbar;
|
||||
const [text, setText] = useState("Loading...");
|
||||
const [isOpenModal, setIsOpenModal] = useState(true);
|
||||
const [isVisualLoading, setIsVisualLoading] = useState(true);
|
||||
const [isDataLoading, setIsDataLoading] = useState(true);
|
||||
|
||||
const handleNext = () => {
|
||||
if (homeConfig.pathFromHome === EPathsFromHome.breath) {
|
||||
@ -68,10 +70,14 @@ function CompatResultPage(): JSX.Element {
|
||||
setTimeout(loadAIRequest, 3000);
|
||||
}
|
||||
setText(aIRequest?.ai_request?.response?.body || "Loading...");
|
||||
setIsDataLoading(false);
|
||||
checkLoading();
|
||||
return aIRequest.ai_request;
|
||||
};
|
||||
return await loadAIRequest();
|
||||
}
|
||||
setIsDataLoading(false);
|
||||
checkLoading();
|
||||
setText(aICompat?.compat?.body || "Loading...");
|
||||
|
||||
return aICompat.compat;
|
||||
@ -91,6 +97,14 @@ function CompatResultPage(): JSX.Element {
|
||||
return "108px";
|
||||
};
|
||||
|
||||
function checkLoading() {
|
||||
if (isVisualLoading || isDataLoading) {
|
||||
setIsOpenModal(true);
|
||||
} else {
|
||||
setIsOpenModal(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section
|
||||
className={`${styles.page} page`}
|
||||
@ -100,7 +114,8 @@ function CompatResultPage(): JSX.Element {
|
||||
<CompatibilityLoading
|
||||
secondPerson={rightUser.name}
|
||||
onEndLoading={() => {
|
||||
setIsOpenModal(false);
|
||||
setIsVisualLoading(false);
|
||||
checkLoading();
|
||||
}}
|
||||
/>
|
||||
</FullScreenModal>
|
||||
|
||||
@ -14,7 +14,7 @@ const colors: Record<string, string> = {
|
||||
}
|
||||
|
||||
const valueFormatter = (value: number) => {
|
||||
return `${(value).toFixed()}%`
|
||||
return `${(value * 10).toFixed(1)}%`
|
||||
}
|
||||
|
||||
function EnergyValues({ className, values }: IEnergyValues): JSX.Element {
|
||||
|
||||
@ -134,7 +134,7 @@ function HomePage(): JSX.Element {
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.content} style={{ marginTop: isShowNavbar ? "calc(100vh - 570px)" : "calc(100vh - 500px)"}}>
|
||||
<div className={styles["content__buttons"]}>
|
||||
{<div className={`${styles["content__buttons"]} ${isShowNavbar ? styles["content__buttons--hidden"] : ""}`}>
|
||||
<BlurringSubstrate
|
||||
style={{ color: "#fa71ea" }}
|
||||
className={styles["content__buttons-item"]}
|
||||
@ -149,7 +149,7 @@ function HomePage(): JSX.Element {
|
||||
>
|
||||
{buttonTextFormatter(t("aura-10_breath-button"))}
|
||||
</BlurringSubstrate>
|
||||
</div>
|
||||
</div>}
|
||||
<div className={styles["content__daily-forecast"]}>
|
||||
{dailyForecast &&
|
||||
dailyForecast.forecasts.map((forecast, index) => (
|
||||
|
||||
@ -131,6 +131,11 @@
|
||||
/* margin-left: 13px; */
|
||||
}
|
||||
|
||||
.content__buttons--hidden {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(0.9);
|
||||
|
||||
@ -3,8 +3,9 @@ import styles from "./styles.module.css";
|
||||
|
||||
export interface INavbarHomeItems {
|
||||
title: string;
|
||||
path: string;
|
||||
paths: string[];
|
||||
image: string;
|
||||
path: string;
|
||||
active?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
@ -32,9 +33,9 @@ function NavbarFooter({ items }: INavbarHomeProps): JSX.Element {
|
||||
className={styles["navbar-item__image-container"]}
|
||||
style={{
|
||||
backgroundColor:
|
||||
item.path === location.pathname ? "#ff2c57" : "#fff",
|
||||
mask: `url('./navbar-icons/${item.image}')`,
|
||||
WebkitMask: `url('./navbar-icons/${item.image}')`,
|
||||
item.paths.includes(location.pathname) ? "#ff2c57" : "#fff",
|
||||
mask: `url('/navbar-icons/${item.image}')`,
|
||||
WebkitMask: `url('/navbar-icons/${item.image}')`,
|
||||
WebkitMaskSize: "contain",
|
||||
WebkitMaskRepeat: "no-repeat",
|
||||
WebkitMaskPosition: "center",
|
||||
@ -49,7 +50,7 @@ function NavbarFooter({ items }: INavbarHomeProps): JSX.Element {
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
color: item.path === location.pathname ? "#ff2c57" : "#fff",
|
||||
color: item.paths.includes(location.pathname) ? "#ff2c57" : "#fff",
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
|
||||
@ -12,8 +12,6 @@ function StartBreathModalChild({
|
||||
isShowNavbar=false
|
||||
}: IStartBreathModalChildProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
console.log(isShowNavbar);
|
||||
|
||||
|
||||
return (
|
||||
<section className={`${styles["start-breath"]} page ${isShowNavbar ? styles["show-navbar"] : ""}`}>
|
||||
|
||||
@ -28,20 +28,27 @@ function UserCallbacksPage(): JSX.Element {
|
||||
if (homeConfig.pathFromHome === EPathsFromHome.breath) {
|
||||
return navigate(routes.client.compatibility());
|
||||
}
|
||||
if (homeConfig.pathFromHome === EPathsFromHome.navbar) {
|
||||
return navigate(routes.client.breath());
|
||||
}
|
||||
};
|
||||
|
||||
const getPaddingBottomPage = () => {
|
||||
if (homeConfig.pathFromHome === EPathsFromHome.compatibility && showNavbarFooter) return "246px";
|
||||
if (
|
||||
homeConfig.pathFromHome === EPathsFromHome.compatibility &&
|
||||
showNavbarFooter
|
||||
)
|
||||
return "246px";
|
||||
if (showNavbarFooter) return "164px";
|
||||
return "108px";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
className={`${styles.page} page`}
|
||||
style={{ paddingBottom: getPaddingBottomPage() }}
|
||||
>
|
||||
{!showNavbarFooter && <div className={styles.cross} onClick={handleNext}></div>}
|
||||
{<div className={styles.cross} onClick={handleNext}></div>}
|
||||
<div className={styles["title-container"]}>
|
||||
<Title variant="h3" className={styles.percent}>
|
||||
<>
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
getCategoryIdByZodiacSign,
|
||||
getZodiacSignByDate,
|
||||
} from "@/services/zodiac-sign";
|
||||
import { Zodiac } from "@/api/resources/Zodiacs";
|
||||
import { Zodiac, ZodiacParagraph } from "@/api/resources/Zodiacs";
|
||||
|
||||
type Forecasts = DailyForecasts.Forecast[];
|
||||
type PersonalAssets = Assets.Asset[];
|
||||
@ -21,9 +21,42 @@ interface WallpaperData {
|
||||
zodiac: Zodiac;
|
||||
}
|
||||
|
||||
function getZodiacParagraphs(
|
||||
paragraphs: ZodiacParagraph[],
|
||||
depth = 0
|
||||
): JSX.Element[] {
|
||||
depth++;
|
||||
if (!paragraphs) {
|
||||
return [];
|
||||
}
|
||||
function getTypeOfContent(content: string[] | ZodiacParagraph[]) {
|
||||
if (Array.isArray(content) && content.length > 0) {
|
||||
return typeof content[0];
|
||||
}
|
||||
}
|
||||
|
||||
return paragraphs.map((paragraph, index) => {
|
||||
const Headline = `h${depth}` as keyof JSX.IntrinsicElements;
|
||||
return (
|
||||
<div className={styles["wallpaper-paragraph"]} key={index}>
|
||||
<Headline
|
||||
className={`${styles["wallpaper-paragraph__title"]} ${
|
||||
depth % 2 === 0 ? styles["wallpaper-paragraph__title--white"] : ""
|
||||
}`}
|
||||
>
|
||||
{paragraph.title}
|
||||
</Headline>
|
||||
{getTypeOfContent(paragraph.content) === "string"
|
||||
? paragraph.content.map((content, _index) => <p key={_index}>{content as string}</p>)
|
||||
: getZodiacParagraphs(paragraph.content as ZodiacParagraph[], depth)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function WallpaperPage(): JSX.Element {
|
||||
const api = useApi();
|
||||
const { t, i18n } = useTranslation();
|
||||
const { i18n } = useTranslation();
|
||||
const locale = i18n.language;
|
||||
const token =
|
||||
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjIzNjEyLCJpYXQiOjE2OTM0MTg5MTAsImV4cCI6MTcwMjA1ODkxMCwianRpIjoiNzg5MjkwYWItODg0YS00MGUyLTkyNjEtOWI2OGEyNjkwNmE0IiwiZW1haWwiOiJvdGhlckBleGFtcGxlLmNvbSIsInN0YXRlIjoicHJvdmVuIiwibG9jIjoiZW4iLCJ0eiI6LTI4ODAwLCJ0eXBlIjoiZW1haWwiLCJpc3MiOiJjb20ubGlmZS5hdXJhIn0.J2ocWIv5jKzuKMcwMgWMiNMyGg5qLlMAeln-bQm_9lw";
|
||||
@ -50,7 +83,6 @@ function WallpaperPage(): JSX.Element {
|
||||
}));
|
||||
}, [api, token, locale, zodiacSign]);
|
||||
const { data, isPending } = useApiCall<WallpaperData>(loadData);
|
||||
const forecasts = data ? data.forecasts : [];
|
||||
const asset = data ? data.assets.at(0) : null;
|
||||
const zodiacInfo = data ? data.zodiac : null;
|
||||
|
||||
@ -64,14 +96,6 @@ function WallpaperPage(): JSX.Element {
|
||||
className={styles["wallpaper-image"]}
|
||||
style={{ backgroundImage: `url(${asset?.url})` }}
|
||||
>
|
||||
<div className={styles["zodiac-metas"]}>
|
||||
{zodiacInfo?.metas.map((meta, index) => (
|
||||
<div key={index} className={styles["zodiac-meta"]}>
|
||||
<p className={styles["zodiac-meta-label"]}>{meta.label}</p>
|
||||
<p className={styles["zodiac-meta-value"]}>{meta.value}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{asset ? (
|
||||
<div className={styles["btn-download"]} onClick={handleClick} />
|
||||
) : null}
|
||||
@ -80,10 +104,18 @@ function WallpaperPage(): JSX.Element {
|
||||
<div className={styles["wallpaper-content"]}>
|
||||
{isPending ? null : (
|
||||
<>
|
||||
<h1 className={styles["wallpaper-title"]}>
|
||||
<div className={styles["zodiac-metas"]}>
|
||||
{zodiacInfo?.metas.map((meta, index) => (
|
||||
<div key={index} className={styles["zodiac-meta"]}>
|
||||
<p className={styles["zodiac-meta-label"]}>{meta.label}</p>
|
||||
<p className={styles["zodiac-meta-value"]}>{meta.value}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* <h1 className={styles["wallpaper-title"]}>
|
||||
{t("analysis_background")}
|
||||
</h1>
|
||||
{forecasts.map((forecast) => (
|
||||
</h1> */}
|
||||
{/* {forecasts.map((forecast) => (
|
||||
<div
|
||||
key={forecast.category_name}
|
||||
className={styles["wallpaper-forecast"]}
|
||||
@ -93,7 +125,10 @@ function WallpaperPage(): JSX.Element {
|
||||
</h2>
|
||||
<p className={styles["wallpaper-text"]}>{forecast.body}</p>
|
||||
</div>
|
||||
))}
|
||||
))} */}
|
||||
<div className={styles["wallpaper-forecast"]}>
|
||||
{getZodiacParagraphs(zodiacInfo?.paragraphs || [])}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
}
|
||||
|
||||
.wallpaper-content::before {
|
||||
content: '';
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
@ -72,7 +72,8 @@
|
||||
}
|
||||
|
||||
.zodiac-metas {
|
||||
width: 100%;
|
||||
width: calc(100% - 64px);
|
||||
margin: -80px auto 16px;
|
||||
height: fit-content;
|
||||
padding: 16px;
|
||||
background-color: #5d5d5d;
|
||||
@ -97,4 +98,20 @@
|
||||
|
||||
.zodiac-meta-value {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.wallpaper-paragraph {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.wallpaper-paragraph__title {
|
||||
color: #ea445a;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.wallpaper-paragraph__title--white {
|
||||
margin-bottom: 0;
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@ import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
export enum EPathsFromHome {
|
||||
compatibility,
|
||||
breath
|
||||
breath,
|
||||
navbar
|
||||
}
|
||||
|
||||
interface ISiteConfig {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user