Browse Source

feat(auth): hydrate marriage profile via root layout providers

Implement server-side extraction of marriage profile data from cookies
to seed the React Query cache during initial application load. This
reduces client-side loading states by providing immediate access to
profile information.

- Extract and parse marriage data in `RootLayout`
- Pass initial profile data to `Providers` component
- Seed `QueryClient` with `initialProfile` using `setQueryData`
- Update `getInitialMarriageProfile` to support data overrides
- Refine loading conditional in `NewMatchClient` to prevent flickering
  when summary data is already present
master
mortezaei 2 weeks ago
parent
commit
f24938d5a1
  1. 11
      src/app/layout.tsx
  2. 2
      src/app/new-match/new-match-client.tsx
  3. 17
      src/app/providers.tsx
  4. 6
      src/hooks/marriage/use-profile-main.ts

11
src/app/layout.tsx

@ -10,6 +10,8 @@ import {
isLocale,
localeDirections,
} from "@/translations/config";
import { getInitialMarriageProfile } from "@/hooks/marriage/use-profile-main";
import type { MarriageProfileResponse } from "@/hooks/marriage/types";
const faminela = localFont({
src: "../../public/fonts/Faminela/Faminela.otf",
@ -90,15 +92,18 @@ export default async function RootLayout({
cookieStore.get("habib_marriage_data")?.value;
let initialMarriageJson: string | null = null;
let initialProfile: MarriageProfileResponse | undefined;
if (rawMarriageCookie) {
try {
const decoded = decodeURIComponent(rawMarriageCookie);
JSON.parse(decoded);
const parsed = JSON.parse(decoded);
initialMarriageJson = decoded;
initialProfile = getInitialMarriageProfile(parsed);
} catch {
try {
JSON.parse(rawMarriageCookie);
const parsed = JSON.parse(rawMarriageCookie);
initialMarriageJson = rawMarriageCookie;
initialProfile = getInitialMarriageProfile(parsed);
} catch {}
}
}
@ -405,7 +410,7 @@ export default async function RootLayout({
className={`${faminela.variable} ${amiri.variable}`}
suppressHydrationWarning
>
<Providers>
<Providers initialProfile={initialProfile}>
{isDevelopment ? <TokenSwitcher /> : null}
<div className="app-shell">{children}</div>
</Providers>

2
src/app/new-match/new-match-client.tsx

@ -734,7 +734,7 @@ export default function NewMatchClient() {
{/* 2. Match Summary Card Section */}
<div className="w-full shrink-0">
{isLoading ? (
{isLoading && !matchSummary ? (
<section className="relative overflow-hidden rounded-[24px] bg-[#FAFBFD] border border-[#F2E5E8] shadow-[0_4px_24px_rgba(0,0,0,0.04)] text-start">
<div className="relative h-[74px] w-full bg-[linear-gradient(180deg,#FCE2E6_0%,#FFD6DC_100%)]" />

17
src/app/providers.tsx

@ -11,16 +11,20 @@ import HardwareBackBridge from "@/components/Componentes/hardware-back-bridge";
import SilentReloader from "@/components/Componentes/silent-reloader";
import { ViewPaddingsProvider } from "@/components/Componentes/view-paddings-provider";
import { marriageQueryKeys } from "@/hooks/marriage/query-keys";
import type { MarriageProfileResponse } from "@/hooks/marriage/types";
// AppFocusReloader was extracted to SilentReloader in silent-reloader.tsx
type ProvidersProps = {
children: ReactNode;
initialProfile?: MarriageProfileResponse;
};
export default function Providers({ children }: ProvidersProps) {
export default function Providers({ children, initialProfile }: ProvidersProps) {
const [queryClient] = useState(
() =>
new QueryClient({
() => {
const qc = new QueryClient({
defaultOptions: {
queries: {
// Keep the last successful response available while a fresh
@ -38,7 +42,12 @@ export default function Providers({ children }: ProvidersProps) {
structuralSharing: true,
},
},
}),
});
if (initialProfile) {
qc.setQueryData(marriageQueryKeys.profile(), initialProfile);
}
return qc;
},
);
return (

6
src/hooks/marriage/use-profile-main.ts

@ -13,8 +13,10 @@ import type { QueryOptions } from "./options";
import { marriageQueryKeys } from "./query-keys";
import type { MarriageProfileResponse } from "./types";
export function getInitialMarriageProfile(): MarriageProfileResponse | undefined {
const data = authBridge.getMarriageData();
export function getInitialMarriageProfile(
dataOverride?: any,
): MarriageProfileResponse | undefined {
const data = dataOverride ?? authBridge.getMarriageData();
if (!data) return undefined;
return {

Loading…
Cancel
Save