Browse Source

refactor(marriage): add visibility control to entry route resolver

Introduces `anonymousEntryVisible` prop to `EntryRouteResolver` to
conditionally prevent redirection to the intro page. Also removes
the legacy `bridgeWaitTimeout` logic and cleans up the effect
dependencies.
Dev
mortezaei 1 week ago
parent
commit
67528f58bb
  1. 10
      src/app/[lang]/page.tsx
  2. 6
      src/components/Componentes/entry-route-resolver.test.tsx
  3. 22
      src/components/Componentes/entry-route-resolver.tsx
  4. 57
      src/components/Componentes/network-image.test.tsx
  5. 30
      src/hooks/marriage/use-profile-main.test.ts

10
src/app/[lang]/page.tsx

@ -1,8 +1,10 @@
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import Intro from "@/app/intro/page";
import EntryRouteResolver from "@/components/Componentes/entry-route-resolver";
import {
getAuthenticatedCachedEntryPath,
isAuthenticatedToken,
MARRIAGE_ENTRY_PATH_COOKIE,
} from "@/lib/entry-route-cache";
import { localizePath } from "@/translations/config";
@ -18,6 +20,7 @@ export default async function LocaleEntryPage({
const token =
cookieStore.get("HABIB_TOKEN")?.value ??
cookieStore.get("habib_token")?.value;
const hasToken = isAuthenticatedToken(token);
const cachedEntryPath = getAuthenticatedCachedEntryPath(
token,
cookieStore.get(MARRIAGE_ENTRY_PATH_COOKIE)?.value,
@ -27,5 +30,10 @@ export default async function LocaleEntryPage({
redirect(localizePath(cachedEntryPath, lang));
}
return <EntryRouteResolver />;
return (
<>
{!hasToken ? <Intro /> : null}
<EntryRouteResolver anonymousEntryVisible={!hasToken} />
</>
);
}

6
src/components/Componentes/entry-route-resolver.test.tsx

@ -107,7 +107,7 @@ describe("EntryRouteResolver", () => {
expect(mocks.refetchProfile).not.toHaveBeenCalled();
});
it("waits for the WebView token event without rendering a loading page", async () => {
it("keeps the server-rendered anonymous entry visible while listening for a WebView token", async () => {
let authenticated = false;
mocks.isAuthenticated.mockImplementation(() => authenticated);
mocks.refetchProfile.mockResolvedValue({
@ -119,7 +119,9 @@ describe("EntryRouteResolver", () => {
});
window.HabibApp = { postMessage: vi.fn() };
const { container } = render(<EntryRouteResolver />);
const { container } = render(
<EntryRouteResolver anonymousEntryVisible />,
);
expect(container).toBeEmptyDOMElement();
expect(mocks.replace).not.toHaveBeenCalled();

22
src/components/Componentes/entry-route-resolver.tsx

@ -15,7 +15,13 @@ import {
import { localizePath } from "@/translations/config";
import { useI18n } from "@/translations/provider";
export default function EntryRouteResolver() {
type EntryRouteResolverProps = {
anonymousEntryVisible?: boolean;
};
export default function EntryRouteResolver({
anonymousEntryVisible = false,
}: EntryRouteResolverProps) {
const router = useRouter();
const { locale } = useI18n();
const { refetch } = useMarriageProfileQuery({
@ -26,10 +32,11 @@ export default function EntryRouteResolver() {
useEffect(() => {
let isActive = true;
let bridgeWaitTimeout: number | undefined;
const goToIntro = () => {
if (!anonymousEntryVisible) {
router.replace(localizePath("/intro", locale));
}
};
const resolveEntryRoute = async () => {
@ -70,10 +77,6 @@ export default function EntryRouteResolver() {
};
const handleTokenChanged = () => {
if (bridgeWaitTimeout !== undefined) {
window.clearTimeout(bridgeWaitTimeout);
bridgeWaitTimeout = undefined;
}
void resolveEntryRoute();
};
@ -81,23 +84,18 @@ export default function EntryRouteResolver() {
if (authBridge.isAuthenticated()) {
void resolveEntryRoute();
} else if (window.HabibApp) {
bridgeWaitTimeout = window.setTimeout(goToIntro, 4_000);
} else {
goToIntro();
}
return () => {
isActive = false;
if (bridgeWaitTimeout !== undefined) {
window.clearTimeout(bridgeWaitTimeout);
}
window.removeEventListener(
HABIB_AUTH_TOKEN_CHANGED_EVENT,
handleTokenChanged,
);
};
}, [locale, refetch, router]);
}, [anonymousEntryVisible, locale, refetch, router]);
return null;
}

57
src/components/Componentes/network-image.test.tsx

@ -0,0 +1,57 @@
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
import { describe, expect, it, afterEach } from "vitest";
import NetworkImage from "./network-image";
describe("NetworkImage", () => {
afterEach(() => {
cleanup();
});
it("renders fallback image when src is not provided", () => {
render(
<NetworkImage
fallbackSrc="/assets/images/Frame 2095586523.png"
alt="fallback image"
width={300}
height={200}
/>,
);
const img = screen.getByAltText("fallback image");
expect(img).toBeDefined();
expect(img.getAttribute("src")).toContain("Frame%202095586523.png");
});
it("renders provided src when available", () => {
render(
<NetworkImage
src="https://example.com/custom-thumbnail.jpg"
fallbackSrc="/assets/images/Frame 2095586523.png"
alt="custom image"
width={300}
height={200}
/>,
);
const img = screen.getByAltText("custom image");
expect(img).toBeDefined();
expect(img.getAttribute("src")).toBe("https://example.com/custom-thumbnail.jpg");
});
it("switches to fallbackSrc on image load error", () => {
render(
<NetworkImage
src="https://example.com/broken-image.jpg"
fallbackSrc="/assets/images/Frame 2095586523.png"
alt="broken image"
width={300}
height={200}
/>,
);
const img = screen.getByAltText("broken image");
fireEvent.error(img);
expect(img.getAttribute("src")).toContain("Frame%202095586523.png");
});
});

30
src/hooks/marriage/use-profile-main.test.ts

@ -45,4 +45,34 @@ describe("getMarriageProfile", () => {
expect(mocks.setCachedEntryPath).toHaveBeenCalledWith(null);
});
it("overwrites the old questions route when the backend advances state", async () => {
mocks.get
.mockResolvedValueOnce({
data: {
status: "pending_info",
gender: "male",
is_registering_for_self: true,
},
})
.mockResolvedValueOnce({
data: {
status: "waiting",
gender: "male",
is_registering_for_self: true,
},
});
await getMarriageProfile();
await getMarriageProfile();
expect(mocks.setCachedEntryPath).toHaveBeenNthCalledWith(
1,
"/questions-list",
);
expect(mocks.setCachedEntryPath).toHaveBeenNthCalledWith(
2,
"/finding-match",
);
});
});
Loading…
Cancel
Save