Browse Source

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.
Dev
mortezaei 1 week ago
parent
commit
15f3951dcd
  1. 52
      src/components/Componentes/network-image.tsx

52
src/components/Componentes/network-image.tsx

@ -1,7 +1,7 @@
"use client"; "use client";
import Image, { type ImageProps } from "next/image"; import Image, { type ImageProps } from "next/image";
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export interface NetworkImageProps extends Omit<ImageProps, "src"> { export interface NetworkImageProps extends Omit<ImageProps, "src"> {
@ -22,20 +22,42 @@ export function NetworkImage({
width, width,
height, height,
priority, priority,
unoptimized,
...props ...props
}: NetworkImageProps) { }: NetworkImageProps) {
const initialSrc = src || fallbackSrc;
const [imgSrc, setImgSrc] = useState<string>(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<string>(targetSrc);
const [isLoaded, setIsLoaded] = useState<boolean>(false);
const [hasError, setHasError] = useState<boolean>(false);
const imgRef = useRef<HTMLImageElement | null>(null);
useEffect(() => { 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]); }, [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 ( return (
<div <div
className={cn( className={cn(
@ -57,23 +79,31 @@ export function NetworkImage({
)} )}
<Image <Image
{...props} {...props}
ref={handleRef}
src={imgSrc} src={imgSrc}
alt={alt} alt={alt}
fill={fill} fill={fill}
width={!fill ? width : undefined} width={!fill ? width : undefined}
height={!fill ? height : undefined} height={!fill ? height : undefined}
priority={priority} priority={priority}
onLoad={() => setIsLoaded(true)}
unoptimized={effectiveUnoptimized}
onLoad={() => {
setIsLoaded(true);
setHasError(false);
}}
onError={() => { onError={() => {
if (imgSrc !== fallbackSrc) { if (imgSrc !== fallbackSrc) {
setImgSrc(fallbackSrc); setImgSrc(fallbackSrc);
setIsLoaded(false);
setHasError(false);
} else { } else {
setHasError(true); setHasError(true);
setIsLoaded(true);
} }
}} }}
className={cn( className={cn(
"transition-opacity duration-300", "transition-opacity duration-300",
!isLoaded ? "opacity-0" : "opacity-100",
!isLoaded && !hasError ? "opacity-0" : "opacity-100",
className, className,
)} )}
/> />

Loading…
Cancel
Save