3 changed files with 152 additions and 7 deletions
-
12src/app/intro/page.tsx
-
16src/components/ui/navigation-button.tsx
-
129src/components/ui/report-actions-sheet.tsx
@ -0,0 +1,129 @@ |
|||||
|
"use client"; |
||||
|
|
||||
|
import { useEffect, useState } from "react"; |
||||
|
import Button from "@/components/ui/button"; |
||||
|
|
||||
|
const EXIT_ANIMATION_MS = 220; |
||||
|
|
||||
|
type ReportActionsSheetProps = { |
||||
|
onClose?: () => void; |
||||
|
}; |
||||
|
|
||||
|
declare global { |
||||
|
interface Window { |
||||
|
HabibApp?: { |
||||
|
postMessage: (message: string) => void; |
||||
|
}; |
||||
|
onFlutterResponse?: (event: { |
||||
|
action: string; |
||||
|
success: boolean; |
||||
|
data?: { latitude: number; longitude: number }; |
||||
|
}) => void; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function ReportActionsSheet({ onClose }: ReportActionsSheetProps) { |
||||
|
const [isVisible, setIsVisible] = useState(true); |
||||
|
const [isEntering, setIsEntering] = useState(true); |
||||
|
const [isClosing, setIsClosing] = useState(false); |
||||
|
|
||||
|
const closeSheet = () => { |
||||
|
if (isClosing) return; |
||||
|
setIsClosing(true); |
||||
|
}; |
||||
|
|
||||
|
const handleGetLocation = () => { |
||||
|
if (typeof window !== "undefined" && window.HabibApp) { |
||||
|
window.HabibApp.postMessage(JSON.stringify({ action: "get_location" })); |
||||
|
closeSheet(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const handleOpenConsultant = () => { |
||||
|
if (typeof window !== "undefined" && window.HabibApp) { |
||||
|
window.HabibApp.postMessage( |
||||
|
JSON.stringify({ |
||||
|
action: "open_consultant_page", |
||||
|
data: { consultant: "habib@gmail.com" }, |
||||
|
}) |
||||
|
); |
||||
|
closeSheet(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const frameId = window.requestAnimationFrame(() => { |
||||
|
setIsEntering(false); |
||||
|
}); |
||||
|
return () => window.cancelAnimationFrame(frameId); |
||||
|
}, []); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
if (!isVisible) return; |
||||
|
const previousBodyOverflow = document.body.style.overflow; |
||||
|
const previousHtmlOverflow = document.documentElement.style.overflow; |
||||
|
document.body.style.overflow = "hidden"; |
||||
|
document.documentElement.style.overflow = "hidden"; |
||||
|
return () => { |
||||
|
document.body.style.overflow = previousBodyOverflow; |
||||
|
document.documentElement.style.overflow = previousHtmlOverflow; |
||||
|
}; |
||||
|
}, [isVisible]); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
if (!isClosing) return; |
||||
|
const timeoutId = window.setTimeout(() => { |
||||
|
setIsVisible(false); |
||||
|
onClose?.(); |
||||
|
}, EXIT_ANIMATION_MS); |
||||
|
return () => window.clearTimeout(timeoutId); |
||||
|
}, [isClosing, onClose]); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
window.onFlutterResponse = (event) => { |
||||
|
if (event.action === "get_location" && event.success && event.data) { |
||||
|
const message = `Location: ${event.data.latitude}, ${event.data.longitude}`; |
||||
|
alert(message); |
||||
|
} |
||||
|
}; |
||||
|
return () => { |
||||
|
window.onFlutterResponse = undefined; |
||||
|
}; |
||||
|
}, []); |
||||
|
|
||||
|
if (!isVisible) return null; |
||||
|
|
||||
|
return ( |
||||
|
<div |
||||
|
className={[ |
||||
|
"fixed inset-0 z-50 flex items-end justify-center transition-all duration-[220ms]", |
||||
|
isClosing || isEntering |
||||
|
? "bg-[#171717]/0 opacity-0" |
||||
|
: "bg-[#171717]/55 opacity-100", |
||||
|
] |
||||
|
.filter(Boolean) |
||||
|
.join(" ")} |
||||
|
role="dialog" |
||||
|
aria-modal="true" |
||||
|
onClick={(event) => { |
||||
|
if (event.target === event.currentTarget) closeSheet(); |
||||
|
}} |
||||
|
> |
||||
|
<section |
||||
|
className={[ |
||||
|
"w-full max-w-[375px] rounded-t-[15px] bg-[#F9F8F8] p-3.5 shadow-[0_20px_60px_rgba(15,23,42,0.08)] transition-transform duration-[220ms] ease-out", |
||||
|
isClosing || isEntering ? "translate-y-full" : "translate-y-0", |
||||
|
] |
||||
|
.filter(Boolean) |
||||
|
.join(" ")} |
||||
|
> |
||||
|
<div className="flex flex-col gap-3"> |
||||
|
<Button onClick={handleGetLocation}>دریافت موقعیت مکانی</Button> |
||||
|
<Button onClick={handleOpenConsultant}>مشاوره با حبیب</Button> |
||||
|
</div> |
||||
|
</section> |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default ReportActionsSheet; |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue