diff --git a/src/lib/geo-region.ts b/src/lib/geo-region.ts index 7948f3f..a8c982e 100644 --- a/src/lib/geo-region.ts +++ b/src/lib/geo-region.ts @@ -79,6 +79,99 @@ function resolvePhoneCodeFromCountryCode(isoCode?: string): string | undefined { return undefined; } +function requestLocationFromFlutterBridge(): Promise { + if (typeof window === "undefined") return Promise.resolve(null); + + const hasFlutterBridge = + typeof (window as any).HabibApp?.postMessage === "function" || + typeof (window as any).flutter_inappwebview?.callHandler === "function"; + + if (!hasFlutterBridge) { + return Promise.resolve(null); + } + + return new Promise((resolve) => { + let settled = false; + let timer: NodeJS.Timeout | null = null; + let unsubscribe: (() => void) | undefined; + + const cleanup = () => { + settled = true; + if (timer) clearTimeout(timer); + if (unsubscribe) unsubscribe(); + }; + + timer = setTimeout(() => { + if (!settled) { + cleanup(); + console.warn("[GEO_BRIDGE] Timeout waiting for Flutter get_location response"); + resolve(null); + } + }, 4000); + + const handleFlutterResponse: NonNullable = ( + event, + ) => { + if (event.action === "get_location") { + cleanup(); + if (event.success && event.data) { + const data = event.data as any; + console.log( + "[GEO_BRIDGE] 📥 Received from Flutter get_location:", + JSON.stringify(data), + ); + + const rawCountry = data.country || data.country_code || ""; + const isoCode = + data.country_code || + (rawCountry && rawCountry.trim().length === 2 + ? rawCountry.trim().toUpperCase() + : undefined); + const phoneCode = resolvePhoneCodeFromCountryCode(isoCode); + const countryName = + resolveCountryName(rawCountry || isoCode, "en") || rawCountry; + const countryFa = + resolveCountryName(rawCountry || isoCode, "fa") || countryName; + + console.log( + `[GEO_BRIDGE] 🌍 Resolved Country: FA=${countryFa} | EN=${countryName} | Code=${isoCode} | City=${data.city || "None"} | IP=${data.ip || "None"}`, + ); + + const region: UserGeoRegion = { + ip: data.ip, + city: data.city, + country: countryName, + countryCode: isoCode, + phoneCode: phoneCode || "+44", + }; + resolve(region); + } else { + resolve(null); + } + } + }; + + if (typeof window.addFlutterResponseListener === "function") { + unsubscribe = window.addFlutterResponseListener(handleFlutterResponse); + } + + try { + console.log("[GEO_BRIDGE] 🚀 Sending { action: 'get_location' } to Flutter..."); + if ((window as any).HabibApp?.postMessage) { + (window as any).HabibApp.postMessage( + JSON.stringify({ action: "get_location" }), + ); + } else if ((window as any).flutter_inappwebview?.callHandler) { + (window as any).flutter_inappwebview.callHandler("get_location"); + } + } catch (err) { + console.error("[GEO_BRIDGE] Failed to postMessage to Flutter:", err); + cleanup(); + resolve(null); + } + }); +} + export function getUserGeoRegion(force = false): Promise { if (!force) { const existing = getStoredUserGeoRegion(); @@ -96,9 +189,18 @@ export function getUserGeoRegion(force = false): Promise { geoRegionPromise = (async () => { try { - // 1. Primary: Habib Backend User Region API (/account/auth/user/region/) + // 1. Primary: Flutter Native Bridge (uses phone's direct native IP) + const bridgeRegion = await requestLocationFromFlutterBridge(); + if (bridgeRegion && (bridgeRegion.country || bridgeRegion.city)) { + setStoredUserGeoRegion(bridgeRegion); + return bridgeRegion; + } + + // 2. Secondary: Direct HTTP Client fallback (if in browser or bridge fails) try { - console.log("[GEO_AUTO_LOG] 🚀 Requesting /account/auth/user/region/ from backend..."); + console.log( + "[GEO_AUTO_LOG] 🚀 Requesting /account/auth/user/region/ from backend...", + ); const response = await http.get<{ ip?: string; country?: string; @@ -109,18 +211,22 @@ export function getUserGeoRegion(force = false): Promise { }); const data = response.data; - console.log("[GEO_AUTO_LOG] 📥 Backend Raw Response:", JSON.stringify(data)); + console.log( + "[GEO_AUTO_LOG] 📥 Backend Raw Response:", + JSON.stringify(data), + ); if (data && (data.country || data.country_code || data.city)) { + const rawCountry = data.country || data.country_code || ""; const isoCode = data.country_code || - (data.country && data.country.trim().length === 2 - ? data.country.trim().toUpperCase() + (rawCountry && rawCountry.trim().length === 2 + ? rawCountry.trim().toUpperCase() : undefined); const phoneCode = resolvePhoneCodeFromCountryCode(isoCode); const countryName = - resolveCountryName(data.country || isoCode, "en") || data.country; + resolveCountryName(rawCountry || isoCode, "en") || rawCountry; const countryFa = - resolveCountryName(data.country || isoCode, "fa") || countryName; + resolveCountryName(rawCountry || isoCode, "fa") || countryName; console.log( `[GEO_AUTO_LOG] 🌍 Resolved Country: FA=${countryFa} | EN=${countryName} | Code=${isoCode} | City=${data.city || "None"} | IP=${data.ip || "None"}`,