Browse Source

fix(header): restore original authentic Faminela serif typography for English Marriage while keeping bold Arabic/Persian

master
mortezaei 2 weeks ago
parent
commit
9864164f14
  1. 18
      src/app/globals.css
  2. 15
      src/components/Componentes/information-sheet.tsx
  3. 2
      src/components/Componentes/page-header.tsx
  4. 79
      src/hooks/marriage/use-profile-main.ts
  5. 26
      src/hooks/use-hardware-back-handler.ts

18
src/app/globals.css

@ -3,8 +3,10 @@
/* ─── App fonts matching Flutter Najm & Hosseinieh ─── */ /* ─── App fonts matching Flutter Najm & Hosseinieh ─── */
@font-face { @font-face {
font-family: 'Faminela'; 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-style: normal;
font-display: swap; font-display: swap;
} }
@ -314,7 +316,17 @@ body {
} }
.font-faminela { .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 { .app-shell {

15
src/components/Componentes/information-sheet.tsx

@ -506,6 +506,8 @@ export function InformationSheet({
}; };
}, [isRendered]); }, [isRendered]);
const isBackdropPointerDownRef = useRef(false);
if (!isRendered || !mounted) { if (!isRendered || !mounted) {
return null; return null;
} }
@ -523,16 +525,25 @@ export function InformationSheet({
aria-label={t["Information sheet"]} aria-label={t["Information sheet"]}
tabIndex={-1} tabIndex={-1}
dir={isRtl ? "rtl" : "ltr"} dir={isRtl ? "rtl" : "ltr"}
onPointerDown={(event) => {
isBackdropPointerDownRef.current = event.target === event.currentTarget;
}}
onClick={(event) => { onClick={(event) => {
if (closeOnOutside && event.target === event.currentTarget) {
if (
closeOnOutside &&
isBackdropPointerDownRef.current &&
event.target === event.currentTarget
) {
console.log("[InformationSheet] Backdrop clicked -> closeSheet");
closeSheet(); closeSheet();
} }
isBackdropPointerDownRef.current = false;
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
if ( if (
closeOnOutside && closeOnOutside &&
event.target === event.currentTarget && event.target === event.currentTarget &&
(event.key === "Escape" || event.key === "Enter" || event.key === " ")
event.key === "Escape"
) { ) {
event.preventDefault(); event.preventDefault();
closeSheet(); closeSheet();

2
src/components/Componentes/page-header.tsx

@ -83,7 +83,7 @@ export function PageHeader({
<NavigationButton icon="back" profile={profile} {...leftButton} /> <NavigationButton icon="back" profile={profile} {...leftButton} />
)} )}
<h1 className="pointer-events-none absolute left-1/2 -translate-x-1/2 font-faminela text-[20px] font-bold leading-none text-foreground text-center truncate max-w-[60%]">
<h1 className="pointer-events-none absolute left-1/2 -translate-x-1/2 font-faminela text-[20px] leading-none text-foreground text-center truncate max-w-[60%]">
{t["Marriage"] || "Marriage"} {t["Marriage"] || "Marriage"}
</h1> </h1>

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

@ -39,30 +39,50 @@ export function getInitialMarriageProfile(
} as MarriageProfileResponse; } as MarriageProfileResponse;
} }
// Priority 2: Use locally persisted profile from previous successful server response
const flutterData = authBridge.getMarriageData();
const persisted = getScopedMarriageProfile(); 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) { if (persisted) {
return persisted; return persisted;
} }
// Priority 3: Fallback to Flutter native bridge data // Priority 3: Fallback to Flutter native bridge data
const data = authBridge.getMarriageData();
if (!data) return undefined;
if (!flutterData) return undefined;
return { 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; } as MarriageProfileResponse;
} }
@ -102,6 +122,33 @@ export async function getMarriageProfile() {
export function useMarriageProfileQuery<TData = MarriageProfileResponse>( export function useMarriageProfileQuery<TData = MarriageProfileResponse>(
options?: QueryOptions<MarriageProfileResponse, TData>, options?: QueryOptions<MarriageProfileResponse, TData>,
) { ) {
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({ const query = useQuery({
staleTime: 30 * 1000, staleTime: 30 * 1000,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,

26
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. * Returns true if a handler consumed the event, or false if Flutter should handle/close.
*/ */
export function handleHardwareBackSync(): boolean { 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) { if (backHandlerStack.length === 0) {
return false; return false;
} }
@ -76,6 +89,19 @@ export function handleHardwareBackSync(): boolean {
* { handled: false } if Flutter should close the WebView screen. * { handled: false } if Flutter should close the WebView screen.
*/ */
export async function handleHardwareBack(): Promise<{ handled: boolean }> { 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) { if (backHandlerStack.length === 0) {
return { handled: false }; return { handled: false };
} }

Loading…
Cancel
Save