You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
9.3 KiB
261 lines
9.3 KiB
"use client";
|
|
|
|
import Image from "next/image";
|
|
import { useRouter } from "next/navigation";
|
|
import { useCallback, useEffect, useState } from "react";
|
|
import Button from "@/components/Componentes/button";
|
|
import NetworkImage from "@/components/Componentes/network-image";
|
|
import PageHeader from "@/components/Componentes/page-header";
|
|
import SectionOverlayHost from "@/components/Componentes/section-overlay-host";
|
|
import SliderPage from "@/components/Componentes/slider-page";
|
|
import VideoPlayer from "@/components/Componentes/video-player";
|
|
import {
|
|
IntroMatchesIcon,
|
|
IntroMarriagesIcon,
|
|
IntroPlayButtonIcon,
|
|
IntroUsersIcon,
|
|
} from "@/components/Componentes/intro-hero-illustration";
|
|
import { useMarriageConfigQuery } from "@/hooks/marriage/use-marriage-config";
|
|
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
|
|
import { useHabibWebReady } from "@/hooks/use-habib-web-ready";
|
|
import { useHardwareBackHandler } from "@/hooks/use-hardware-back-handler";
|
|
import { authBridge } from "@/lib/auth-bridge";
|
|
import { SHOW_INTRO_VIDEOS } from "@/config/features";
|
|
import { getSubmitPath } from "@/lib/get-submit-path";
|
|
import { localizePath } from "@/translations/config";
|
|
import { useI18n } from "@/translations/provider";
|
|
|
|
import { PageBackground } from "@/components/Componentes/page-background";
|
|
|
|
export default function IntroClient() {
|
|
const router = useRouter();
|
|
const { dictionary: t, locale } = useI18n();
|
|
const { data: profile, refetch } = useMarriageProfileQuery({
|
|
enabled: false,
|
|
retry: false,
|
|
});
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [isPlayerOpen, setIsPlayerOpen] = useState(false);
|
|
const [isStepsOpen, setIsStepsOpen] = useState(false);
|
|
|
|
const { data: config } = useMarriageConfigQuery();
|
|
|
|
// Signal Flutter that the Intro UI is ready. Config has a local fallback
|
|
// image so we don't need to wait for it — announce immediately on mount.
|
|
useHabibWebReady(true);
|
|
|
|
useEffect(() => {
|
|
const readStepsFromUrl = () => {
|
|
if (typeof window === "undefined") return;
|
|
const params = new URLSearchParams(window.location.search);
|
|
setIsStepsOpen(params.get("steps") === "open");
|
|
};
|
|
|
|
readStepsFromUrl();
|
|
window.addEventListener("popstate", readStepsFromUrl);
|
|
return () => window.removeEventListener("popstate", readStepsFromUrl);
|
|
}, []);
|
|
|
|
const handleOpenSteps = useCallback(() => {
|
|
setIsStepsOpen(true);
|
|
if (typeof window !== "undefined") {
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.set("steps", "open");
|
|
window.history.replaceState({ steps: "open" }, "", url.toString());
|
|
}
|
|
}, []);
|
|
|
|
const handleCloseSteps = useCallback(() => {
|
|
setIsStepsOpen(false);
|
|
if (typeof window !== "undefined") {
|
|
const params = new URLSearchParams(window.location.search);
|
|
if (params.get("steps") === "open") {
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.delete("steps");
|
|
window.history.replaceState({}, "", url.toString());
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
useHardwareBackHandler(() => {
|
|
if (isStepsOpen) {
|
|
handleCloseSteps();
|
|
return true; // handled: keep WebView open, just close the overlay
|
|
}
|
|
return false; // Root: tell Flutter to close WebView screen
|
|
}, isStepsOpen);
|
|
|
|
const handleSubmit = async () => {
|
|
if (isSubmitting) {
|
|
return;
|
|
}
|
|
|
|
setIsSubmitting(true);
|
|
|
|
try {
|
|
if (!authBridge.isAuthenticated()) {
|
|
const token = await authBridge.ensureToken();
|
|
if (!token) {
|
|
console.warn("No token from bridge – login was not completed");
|
|
return;
|
|
}
|
|
}
|
|
|
|
let profileResponse = profile;
|
|
try {
|
|
const { data: freshProfile } = await refetch();
|
|
profileResponse = freshProfile ?? profile;
|
|
} catch (refetchError) {
|
|
console.warn(
|
|
"Could not refetch profile data – using fallback",
|
|
refetchError,
|
|
);
|
|
}
|
|
|
|
const submitPath = getSubmitPath(profileResponse);
|
|
if (submitPath === "/intro" || submitPath === "/terms") {
|
|
handleOpenSteps();
|
|
} else {
|
|
router.push(localizePath(submitPath, locale));
|
|
}
|
|
} catch (error) {
|
|
console.error("Submission/redirect failed", error);
|
|
handleOpenSteps();
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<PageBackground />
|
|
|
|
<PageHeader profile={profile} sticky />
|
|
|
|
<main
|
|
style={{
|
|
minHeight: SHOW_INTRO_VIDEOS
|
|
? undefined
|
|
: "calc(100dvh - 56px - max(12px, calc(var(--safe-top) + 6px)))",
|
|
}}
|
|
className={[
|
|
"flex min-h-0 flex-1 flex-col text-center pb-[calc(90px+var(--safe-bottom))]",
|
|
SHOW_INTRO_VIDEOS ? "pt-4" : "justify-center",
|
|
].join(" ")}
|
|
>
|
|
<div className="flex flex-col items-center">
|
|
<Image
|
|
src={"/assets/images/Group 1597880466.svg"}
|
|
alt={t["heavenly marriage"]}
|
|
width={168}
|
|
height={155}
|
|
priority
|
|
loading="eager"
|
|
/>
|
|
<h2 className="group-16 text-[#475569] font-bold mt-5 text-center">
|
|
{t["A Path to Heavenly Marriage"]}
|
|
</h2>
|
|
<p className="text-center mt-2 group-12 text-[#4D4D4D]">
|
|
{
|
|
t[
|
|
'We have come together with the goal of creating a secure and confidential path for "permanent marriage" among Muslims'
|
|
]
|
|
}
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center justify-between gap-2 mt-8 w-full">
|
|
<div className="flex min-w-0 flex-1 items-center gap-1 p-2 border border-[#FD6ABB]/10 rounded-2xl">
|
|
<IntroUsersIcon className="shrink-0" />
|
|
<div className="flex min-w-0 flex-1 flex-col items-start justify-center">
|
|
<p className="w-full truncate text-start text-[#36363C] group-16 font-bold leading-tight" title="120">
|
|
120
|
|
</p>
|
|
<p
|
|
className="w-full truncate text-start text-[#36363C] group-10 font-semibold capitalize"
|
|
title={t["user profiles"]}
|
|
>
|
|
{t["user profiles"]}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex min-w-0 flex-1 items-center gap-1 p-2 border border-[#FD6ABB]/10 rounded-2xl">
|
|
<IntroMatchesIcon className="shrink-0" />
|
|
<div className="flex min-w-0 flex-1 flex-col items-start justify-center">
|
|
<p className="w-full truncate text-start text-[#36363C] group-16 font-bold leading-tight" title="14">
|
|
14
|
|
</p>
|
|
<p
|
|
className="w-full truncate text-start text-[#36363C] group-10 font-semibold capitalize"
|
|
title={t["matches"]}
|
|
>
|
|
{t["matches"]}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex min-w-0 flex-1 items-center gap-1 p-2 border border-[#FD6ABB]/10 rounded-2xl">
|
|
<IntroMarriagesIcon className="shrink-0" />
|
|
<div className="flex min-w-0 flex-1 flex-col items-start justify-center">
|
|
<p className="w-full truncate text-start text-[#36363C] group-16 font-bold leading-tight" title="14">
|
|
14
|
|
</p>
|
|
<p
|
|
className="w-full truncate text-start text-[#36363C] group-10 font-semibold capitalize"
|
|
title={t["marriages"]}
|
|
>
|
|
{t["marriages"]}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{SHOW_INTRO_VIDEOS && (
|
|
<>
|
|
<div
|
|
className="mt-14 relative cursor-pointer group rounded-2xl overflow-hidden aspect-[344/221] max-w-[344px] w-full mx-auto"
|
|
onClick={() => setIsPlayerOpen(true)}
|
|
>
|
|
<NetworkImage
|
|
src={config?.intro_video_thumbnail_url}
|
|
fallbackSrc="/assets/images/Frame 2095586523.png"
|
|
alt={t["video"]}
|
|
fill
|
|
className="object-cover transition-transform duration-300 group-hover:scale-105"
|
|
priority
|
|
/>
|
|
<div className="absolute inset-0 bg-black/10 transition-colors duration-300 group-hover:bg-black/20" />
|
|
<IntroPlayButtonIcon className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transition-transform duration-300 group-hover:scale-110 active:scale-95" />
|
|
</div>
|
|
|
|
<VideoPlayer
|
|
isOpen={isPlayerOpen}
|
|
onClose={() => setIsPlayerOpen(false)}
|
|
videoUrl={config?.intro_video_url}
|
|
/>
|
|
</>
|
|
)}
|
|
<div className="pointer-events-none fixed bottom-0 left-1/2 z-20 w-full max-w-[834px] -translate-x-1/2 bg-[#F5F5F5]">
|
|
<div
|
|
style={{ paddingBottom: "calc(20px + var(--safe-bottom))" }}
|
|
className="pointer-events-auto px-[17px] md:px-8 pt-3"
|
|
>
|
|
<Button
|
|
onClick={handleSubmit}
|
|
disabled={isSubmitting}
|
|
isLoading={isSubmitting}
|
|
>
|
|
{t["Submit"]}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<SectionOverlayHost
|
|
open={isStepsOpen}
|
|
onClose={handleCloseSteps}
|
|
>
|
|
{isStepsOpen && (
|
|
<SliderPage onClose={handleCloseSteps} />
|
|
)}
|
|
</SectionOverlayHost>
|
|
</>
|
|
);
|
|
}
|