diff --git a/src/app/globals.css b/src/app/globals.css
index d28f2c4..eb4e4fd 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -3,8 +3,10 @@
/* ─── App fonts matching Flutter Najm & Hosseinieh ─── */
@font-face {
font-family: 'Faminela';
- src: url('/assets/fonts/faminela.ttf') format('truetype');
- font-weight: 400;
+ src: url('/fonts/Faminela/Faminela.otf') format('opentype'),
+ url('/assets/fonts/Faminela.otf') format('opentype'),
+ url('/assets/fonts/faminela.ttf') format('truetype');
+ font-weight: 400 900;
font-style: normal;
font-display: swap;
}
@@ -314,7 +316,17 @@ body {
}
.font-faminela {
- font-family: var(--marriage-font-brand);
+ font-family: 'Faminela', var(--marriage-font-sans);
+ font-weight: 400;
+}
+
+html[lang='fa'] .font-faminela,
+html[lang='ar'] .font-faminela,
+body[data-lang='fa'] .font-faminela,
+body[data-lang='ar'] .font-faminela,
+.lang-fa .font-faminela,
+.lang-ar .font-faminela {
+ font-weight: 700;
}
.app-shell {
diff --git a/src/components/Componentes/information-sheet.tsx b/src/components/Componentes/information-sheet.tsx
index 3327b8b..69c2728 100644
--- a/src/components/Componentes/information-sheet.tsx
+++ b/src/components/Componentes/information-sheet.tsx
@@ -506,6 +506,8 @@ export function InformationSheet({
};
}, [isRendered]);
+ const isBackdropPointerDownRef = useRef(false);
+
if (!isRendered || !mounted) {
return null;
}
@@ -523,16 +525,25 @@ export function InformationSheet({
aria-label={t["Information sheet"]}
tabIndex={-1}
dir={isRtl ? "rtl" : "ltr"}
+ onPointerDown={(event) => {
+ isBackdropPointerDownRef.current = event.target === event.currentTarget;
+ }}
onClick={(event) => {
- if (closeOnOutside && event.target === event.currentTarget) {
+ if (
+ closeOnOutside &&
+ isBackdropPointerDownRef.current &&
+ event.target === event.currentTarget
+ ) {
+ console.log("[InformationSheet] Backdrop clicked -> closeSheet");
closeSheet();
}
+ isBackdropPointerDownRef.current = false;
}}
onKeyDown={(event) => {
if (
closeOnOutside &&
event.target === event.currentTarget &&
- (event.key === "Escape" || event.key === "Enter" || event.key === " ")
+ event.key === "Escape"
) {
event.preventDefault();
closeSheet();
diff --git a/src/components/Componentes/page-header.tsx b/src/components/Componentes/page-header.tsx
index 9a3d2a4..198c986 100644
--- a/src/components/Componentes/page-header.tsx
+++ b/src/components/Componentes/page-header.tsx
@@ -83,7 +83,7 @@ export function PageHeader({
)}
-
+
{t["Marriage"] || "Marriage"}
diff --git a/src/hooks/marriage/use-profile-main.ts b/src/hooks/marriage/use-profile-main.ts
index 84e8fa5..340b051 100644
--- a/src/hooks/marriage/use-profile-main.ts
+++ b/src/hooks/marriage/use-profile-main.ts
@@ -39,30 +39,50 @@ export function getInitialMarriageProfile(
} as MarriageProfileResponse;
}
- // Priority 2: Use locally persisted profile from previous successful server response
+ const flutterData = authBridge.getMarriageData();
const persisted = getScopedMarriageProfile();
+
+ // If Flutter provided match_summary (e.g. fresh match on new/current device) and persisted doesn't have it, prefer Flutter data
+ if (flutterData?.match_summary && !persisted?.match_summary) {
+ return {
+ id: flutterData.id ?? persisted?.id ?? 0,
+ status: flutterData.status ?? persisted?.status,
+ gender: flutterData.gender ?? persisted?.gender,
+ age: flutterData.age ?? persisted?.age ?? null,
+ is_registering_for_self: flutterData.is_registering_for_self ?? persisted?.is_registering_for_self ?? null,
+ can_edit_profile: flutterData.can_edit_profile ?? persisted?.can_edit_profile ?? true,
+ can_message_expert: flutterData.can_message_expert ?? persisted?.can_message_expert ?? true,
+ active_subscription: flutterData.active_subscription ?? persisted?.active_subscription ?? null,
+ is_ready_for_match: flutterData.is_ready_for_match ?? persisted?.is_ready_for_match ?? true,
+ active_case: flutterData.active_case ?? persisted?.active_case ?? null,
+ needs_subscription: flutterData.needs_subscription ?? persisted?.needs_subscription ?? false,
+ recommended_plan: flutterData.recommended_plan ?? persisted?.recommended_plan ?? null,
+ match_summary: flutterData.match_summary ?? null,
+ } as MarriageProfileResponse;
+ }
+
+ // Priority 2: Use locally persisted profile from previous successful server response
if (persisted) {
return persisted;
}
// Priority 3: Fallback to Flutter native bridge data
- const data = authBridge.getMarriageData();
- if (!data) return undefined;
+ if (!flutterData) return undefined;
return {
- id: data.id ?? 0,
- status: data.status,
- gender: data.gender,
- age: data.age ?? null,
- is_registering_for_self: data.is_registering_for_self ?? null,
- can_edit_profile: data.can_edit_profile ?? true,
- can_message_expert: data.can_message_expert ?? true,
- active_subscription: data.active_subscription ?? null,
- is_ready_for_match: data.is_ready_for_match ?? true,
- active_case: data.active_case ?? null,
- needs_subscription: data.needs_subscription ?? false,
- recommended_plan: data.recommended_plan ?? null,
- match_summary: data.match_summary ?? null,
+ id: flutterData.id ?? 0,
+ status: flutterData.status,
+ gender: flutterData.gender,
+ age: flutterData.age ?? null,
+ is_registering_for_self: flutterData.is_registering_for_self ?? null,
+ can_edit_profile: flutterData.can_edit_profile ?? true,
+ can_message_expert: flutterData.can_message_expert ?? true,
+ active_subscription: flutterData.active_subscription ?? null,
+ is_ready_for_match: flutterData.is_ready_for_match ?? true,
+ active_case: flutterData.active_case ?? null,
+ needs_subscription: flutterData.needs_subscription ?? false,
+ recommended_plan: flutterData.recommended_plan ?? null,
+ match_summary: flutterData.match_summary ?? null,
} as MarriageProfileResponse;
}
@@ -102,6 +122,33 @@ export async function getMarriageProfile() {
export function useMarriageProfileQuery(
options?: QueryOptions,
) {
+ const queryClient = useQueryClient();
+
+ useEffect(() => {
+ const handleInitialData = (e: any) => {
+ const data = e.detail;
+ if (data) {
+ queryClient.setQueryData(
+ marriageQueryKeys.profile(),
+ (old: MarriageProfileResponse | undefined) => {
+ if (!old) return getInitialMarriageProfile(data);
+ return {
+ ...old,
+ ...data,
+ match_summary: data.match_summary ?? old.match_summary,
+ active_case: data.active_case ?? old.active_case,
+ };
+ },
+ );
+ }
+ };
+
+ window.addEventListener("habib:marriage-initial-data", handleInitialData);
+ return () => {
+ window.removeEventListener("habib:marriage-initial-data", handleInitialData);
+ };
+ }, [queryClient]);
+
const query = useQuery({
staleTime: 30 * 1000,
refetchOnWindowFocus: false,
diff --git a/src/hooks/use-hardware-back-handler.ts b/src/hooks/use-hardware-back-handler.ts
index 059f66d..cfdeff6 100644
--- a/src/hooks/use-hardware-back-handler.ts
+++ b/src/hooks/use-hardware-back-handler.ts
@@ -52,6 +52,19 @@ export function useHardwareBackHandler(
* Returns true if a handler consumed the event, or false if Flutter should handle/close.
*/
export function handleHardwareBackSync(): boolean {
+ if (typeof document !== "undefined" && document.activeElement instanceof HTMLElement) {
+ const tag = document.activeElement.tagName;
+ if (
+ tag === "INPUT" ||
+ tag === "TEXTAREA" ||
+ document.activeElement.isContentEditable
+ ) {
+ console.log("[HardwareBackSync] Active input focused — blurring input instead of navigating back");
+ document.activeElement.blur();
+ return true;
+ }
+ }
+
if (backHandlerStack.length === 0) {
return false;
}
@@ -76,6 +89,19 @@ export function handleHardwareBackSync(): boolean {
* { handled: false } if Flutter should close the WebView screen.
*/
export async function handleHardwareBack(): Promise<{ handled: boolean }> {
+ if (typeof document !== "undefined" && document.activeElement instanceof HTMLElement) {
+ const tag = document.activeElement.tagName;
+ if (
+ tag === "INPUT" ||
+ tag === "TEXTAREA" ||
+ document.activeElement.isContentEditable
+ ) {
+ console.log("[HardwareBack] Active input focused — blurring input instead of navigating back");
+ document.activeElement.blur();
+ return { handled: true };
+ }
+ }
+
if (backHandlerStack.length === 0) {
return { handled: false };
}