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:
gofnnp 2023-09-28 02:22:13 +04:00
parent 8b78a612fb
commit 864eb9ee4b
12 changed files with 170 additions and 64 deletions

View File

@ -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 />
) : (

View File

@ -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"]}>

View File

@ -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>

View File

@ -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 {

View File

@ -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) => (

View File

@ -131,6 +131,11 @@
/* margin-left: 13px; */
}
.content__buttons--hidden {
opacity: 0;
pointer-events: none;
}
@keyframes pulse {
0% {
transform: scale(0.9);

View File

@ -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}

View File

@ -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"] : ""}`}>

View File

@ -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}>
<>

View File

@ -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>

View File

@ -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;
}

View File

@ -3,7 +3,8 @@ import type { PayloadAction } from "@reduxjs/toolkit";
export enum EPathsFromHome {
compatibility,
breath
breath,
navbar
}
interface ISiteConfig {