From 15f3951dcd8a9b4be9d58f62d84a36699133a2dd Mon Sep 17 00:00:00 2001 From: mortezaei Date: Sat, 15 Aug 2026 14:34:59 +0330 Subject: [PATCH] feat(marriage): implement persistent entry route caching and improved onboarding flow Introduces a mechanism to cache and resolve the user's entry route based on their authentication status and profile progress. This ensures users are redirected to their correct application state (e.g., questions list, finding match, or intro) more reliably and efficiently. Key changes: - Added `entry-route-cache` utility to manage `HABIB_MARRIAGE_ENTRY_PATH` cookie with path validation. - Implemented `EntryRouteResolver` component to handle initial routing logic and bridge synchronization. - Refactored `getSubmitPath` and `hasCompletedMarriageProfileBasics` to distinguish between incomplete onboarding (terms) and completed basics (questions list). - Updated `authBridge` and `use-profile-main` to trigger cache updates on token changes or profile fetches. - Improved `SliderPage` navigation guards to prevent skipping terms/rules. - Added `NetworkImage` component for better handling of remote image loading and fallbacks. - Enhanced `LoadingSkeleton` with a new `white` shimmer variant. - Updated `TokenSwitcher` to clear cached routes on logout/switch. - Added comprehensive unit tests for new routing and auth logic. --- src/components/Componentes/network-image.tsx | 52 +++++++++++++++----- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/src/components/Componentes/network-image.tsx b/src/components/Componentes/network-image.tsx index 73eca42..a7fcaaf 100644 --- a/src/components/Componentes/network-image.tsx +++ b/src/components/Componentes/network-image.tsx @@ -1,7 +1,7 @@ "use client"; import Image, { type ImageProps } from "next/image"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { cn } from "@/lib/utils"; export interface NetworkImageProps extends Omit { @@ -22,20 +22,42 @@ export function NetworkImage({ width, height, priority, + unoptimized, ...props }: NetworkImageProps) { - const initialSrc = src || fallbackSrc; - const [imgSrc, setImgSrc] = useState(initialSrc); - const [isLoaded, setIsLoaded] = useState(false); - const [hasError, setHasError] = useState(false); + const targetSrc = + src && typeof src === "string" && src.trim() ? src.trim() : fallbackSrc; + const [imgSrc, setImgSrc] = useState(targetSrc); + const [isLoaded, setIsLoaded] = useState(false); + const [hasError, setHasError] = useState(false); + const imgRef = useRef(null); useEffect(() => { - const target = src || fallbackSrc; - setImgSrc(target); - setHasError(false); - setIsLoaded(false); + const nextTarget = + src && typeof src === "string" && src.trim() ? src.trim() : fallbackSrc; + setImgSrc((prev) => { + if (prev !== nextTarget) { + setIsLoaded(false); + setHasError(false); + return nextTarget; + } + return prev; + }); }, [src, fallbackSrc]); + const isExternal = + typeof imgSrc === "string" && + (imgSrc.startsWith("http://") || imgSrc.startsWith("https://")); + const effectiveUnoptimized = + unoptimized !== undefined ? unoptimized : isExternal; + + const handleRef = (node: HTMLImageElement | null) => { + imgRef.current = node; + if (node?.complete && node.naturalWidth > 0 && !isLoaded) { + setIsLoaded(true); + } + }; + return (
setIsLoaded(true)} + unoptimized={effectiveUnoptimized} + onLoad={() => { + setIsLoaded(true); + setHasError(false); + }} onError={() => { if (imgSrc !== fallbackSrc) { setImgSrc(fallbackSrc); + setIsLoaded(false); + setHasError(false); } else { setHasError(true); + setIsLoaded(true); } }} className={cn( "transition-opacity duration-300", - !isLoaded ? "opacity-0" : "opacity-100", + !isLoaded && !hasError ? "opacity-0" : "opacity-100", className, )} />