diff --git a/src/app/intro/intro-client.tsx b/src/app/intro/intro-client.tsx index 000fcdf..a2dc838 100644 --- a/src/app/intro/intro-client.tsx +++ b/src/app/intro/intro-client.tsx @@ -58,20 +58,20 @@ export default function IntroClient() { if (typeof window !== "undefined") { const url = new URL(window.location.href); url.searchParams.set("steps", "open"); - window.history.pushState({ steps: "open" }, "", url.toString()); + 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") { - setIsStepsOpen(false); - window.history.back(); - return; + const url = new URL(window.location.href); + url.searchParams.delete("steps"); + window.history.replaceState({}, "", url.toString()); } } - setIsStepsOpen(false); }, []); useHardwareBackHandler(() => { diff --git a/src/components/Componentes/marriage-advisors-overlay.tsx b/src/components/Componentes/marriage-advisors-overlay.tsx index beb93e5..54e4753 100644 --- a/src/components/Componentes/marriage-advisors-overlay.tsx +++ b/src/components/Componentes/marriage-advisors-overlay.tsx @@ -34,20 +34,20 @@ export function useMarriageAdvisorsOverlay() { if (typeof window !== "undefined") { const url = new URL(window.location.href); url.searchParams.set("advisors", "open"); - window.history.pushState({ advisors: "open" }, "", url.toString()); + window.history.replaceState({ advisors: "open" }, "", url.toString()); } }, []); const closeAdvisors = useCallback(() => { + setIsAdvisorOpen(false); if (typeof window !== "undefined") { const params = new URLSearchParams(window.location.search); if (params.get("advisors") === "open") { - setIsAdvisorOpen(false); - window.history.back(); - return; + const url = new URL(window.location.href); + url.searchParams.delete("advisors"); + window.history.replaceState({}, "", url.toString()); } } - setIsAdvisorOpen(false); }, []); // Intercept hardware back in Flutter WebView when advisor overlay is open diff --git a/src/components/Componentes/match-profile-overlay.tsx b/src/components/Componentes/match-profile-overlay.tsx index 39c14cd..f39d578 100644 --- a/src/components/Componentes/match-profile-overlay.tsx +++ b/src/components/Componentes/match-profile-overlay.tsx @@ -34,20 +34,20 @@ export function useMatchProfileOverlay() { if (typeof window !== "undefined") { const url = new URL(window.location.href); url.searchParams.set("profile", "open"); - window.history.pushState({ profile: "open" }, "", url.toString()); + window.history.replaceState({ profile: "open" }, "", url.toString()); } }, []); const closeProfile = useCallback(() => { + setIsProfileOpen(false); if (typeof window !== "undefined") { const params = new URLSearchParams(window.location.search); if (params.get("profile") === "open") { - setIsProfileOpen(false); - window.history.back(); - return; + const url = new URL(window.location.href); + url.searchParams.delete("profile"); + window.history.replaceState({}, "", url.toString()); } } - setIsProfileOpen(false); }, []); // Intercept hardware back in Flutter WebView when profile overlay is open diff --git a/src/components/Componentes/question-birthplace.tsx b/src/components/Componentes/question-birthplace.tsx index 5ca8b53..a6c484f 100644 --- a/src/components/Componentes/question-birthplace.tsx +++ b/src/components/Componentes/question-birthplace.tsx @@ -249,7 +249,7 @@ export function QuestionBirthplace({ setIsClosing(false); }, [disabled]); - useSheetScrollLock(isOpen, { onBack: closeSheet }); + useSheetScrollLock(isOpen && !isClosing, { onBack: closeSheet }); // Handle escape key useEffect(() => { diff --git a/src/components/Componentes/question-date-sheet.tsx b/src/components/Componentes/question-date-sheet.tsx index 7ecde83..9a1e250 100644 --- a/src/components/Componentes/question-date-sheet.tsx +++ b/src/components/Componentes/question-date-sheet.tsx @@ -181,7 +181,7 @@ export function QuestionDateSheet({ window.setTimeout(onClose, EXIT_ANIMATION_MS); }, [onClose]); - useSheetScrollLock(true, { onBack: closeSheet }); + useSheetScrollLock(!isClosing, { onBack: closeSheet }); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { diff --git a/src/components/Componentes/question-phone.tsx b/src/components/Componentes/question-phone.tsx index 0617f7b..580c316 100644 --- a/src/components/Componentes/question-phone.tsx +++ b/src/components/Componentes/question-phone.tsx @@ -586,7 +586,7 @@ export function QuestionPhone({ ); }, [countryList, searchQuery]); - useSheetScrollLock(isOpen, { onBack: closeSheet }); + useSheetScrollLock(isOpen && !isClosing, { onBack: closeSheet }); useEffect(() => { if (!isOpen) return; diff --git a/src/components/Componentes/question-sheet.test.tsx b/src/components/Componentes/question-sheet.test.tsx index 497c82b..cf07d00 100644 --- a/src/components/Componentes/question-sheet.test.tsx +++ b/src/components/Componentes/question-sheet.test.tsx @@ -192,7 +192,7 @@ describe("QuestionSheet component", () => { }); }); - it("closes the sheet instead of leaving the page on browser Back", async () => { + it("closes the sheet instead of leaving the page on hardware Back", async () => { const question = { id: "q_back", title: "کشور", @@ -202,7 +202,6 @@ describe("QuestionSheet component", () => { options: [{ id: "iran", value: "Iran", label: "ایران", order: 1 }], ui_config: {}, } as QuestionField; - const pushState = vi.spyOn(window.history, "pushState"); render( @@ -213,15 +212,17 @@ describe("QuestionSheet component", () => { ); fireEvent.click(screen.getByRole("button", { name: /انتخاب کشور/i })); - await waitFor(() => expect(pushState).toHaveBeenCalledTimes(1)); + await waitFor(() => { + expect(screen.getByRole("dialog")).toBeInTheDocument(); + }); - window.history.replaceState(null, "", window.location.href); - window.dispatchEvent(new PopStateEvent("popstate")); + const { handleHardwareBackSync } = await import("@/hooks/use-hardware-back-handler"); + const handled = handleHardwareBackSync(); + expect(handled).toBe(true); await waitFor(() => { expect(screen.queryByRole("dialog")).toBeNull(); }); - pushState.mockRestore(); }); it("opens a searchable sheet for 7+ options without focusing search", () => { diff --git a/src/components/Componentes/use-sheet-scroll-lock.ts b/src/components/Componentes/use-sheet-scroll-lock.ts index 72d4854..aa9baff 100644 --- a/src/components/Componentes/use-sheet-scroll-lock.ts +++ b/src/components/Componentes/use-sheet-scroll-lock.ts @@ -10,7 +10,6 @@ let initialHtmlOverflow = ""; let initialAppShellOverflow = ""; let initialAppShellTouchAction = ""; let lockedAppShell: HTMLElement | null = null; -const SHEET_HISTORY_KEY = "__habibQuestionSheet"; type SheetScrollLockOptions = { onBack?: () => void; @@ -25,7 +24,7 @@ export function useSheetScrollLock( onBackRef.current = onBack; // Register in the hardware-back handler stack so that Flutter's - // __habibHandleHardwareBack() closes the sheet instead of navigating. + // __habibHandleHardwareBackSync() closes the sheet instead of exiting the screen. const handleHardwareBack = useCallback(() => { if (onBackRef.current) { onBackRef.current(); @@ -39,18 +38,6 @@ export function useSheetScrollLock( useEffect(() => { if (!isOpen) return; - const historyState = window.history.state; - const ownsHistoryEntry = - historyState?.[SHEET_HISTORY_KEY] !== true && activeSheetCount === 0; - - if (ownsHistoryEntry) { - window.history.pushState( - { ...(historyState ?? {}), [SHEET_HISTORY_KEY]: true }, - "", - window.location.href, - ); - } - if (activeSheetCount === 0) { bodyHadDropdownClass = document.body.classList.contains("dropdown-open"); initialBodyOverflow = document.body.style.overflow; @@ -70,23 +57,9 @@ export function useSheetScrollLock( lockedAppShell.style.touchAction = "none"; } - const handlePopState = () => { - if (ownsHistoryEntry) { - onBackRef.current?.(); - } - }; - window.addEventListener("popstate", handlePopState); - return () => { - window.removeEventListener("popstate", handlePopState); activeSheetCount = Math.max(0, activeSheetCount - 1); if (activeSheetCount === 0) { - if ( - ownsHistoryEntry && - window.history.state?.[SHEET_HISTORY_KEY] === true - ) { - window.history.back(); - } document.body.style.overflow = initialBodyOverflow; document.documentElement.style.overflow = initialHtmlOverflow; if (lockedAppShell) {