Browse Source

feat(geo): integrate flutter bridge get_location to fetch native region from phone

master
mortezaei 4 days ago
parent
commit
9d96917f6a
  1. 120
      src/lib/geo-region.ts

120
src/lib/geo-region.ts

@ -79,6 +79,99 @@ function resolvePhoneCodeFromCountryCode(isoCode?: string): string | undefined {
return undefined; return undefined;
} }
function requestLocationFromFlutterBridge(): Promise<UserGeoRegion | null> {
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<Window["onFlutterResponse"]> = (
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<UserGeoRegion> { export function getUserGeoRegion(force = false): Promise<UserGeoRegion> {
if (!force) { if (!force) {
const existing = getStoredUserGeoRegion(); const existing = getStoredUserGeoRegion();
@ -96,9 +189,18 @@ export function getUserGeoRegion(force = false): Promise<UserGeoRegion> {
geoRegionPromise = (async () => { geoRegionPromise = (async () => {
try { 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 { 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<{ const response = await http.get<{
ip?: string; ip?: string;
country?: string; country?: string;
@ -109,18 +211,22 @@ export function getUserGeoRegion(force = false): Promise<UserGeoRegion> {
}); });
const data = response.data; 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)) { if (data && (data.country || data.country_code || data.city)) {
const rawCountry = data.country || data.country_code || "";
const isoCode = const isoCode =
data.country_code || data.country_code ||
(data.country && data.country.trim().length === 2
? data.country.trim().toUpperCase()
(rawCountry && rawCountry.trim().length === 2
? rawCountry.trim().toUpperCase()
: undefined); : undefined);
const phoneCode = resolvePhoneCodeFromCountryCode(isoCode); const phoneCode = resolvePhoneCodeFromCountryCode(isoCode);
const countryName = const countryName =
resolveCountryName(data.country || isoCode, "en") || data.country;
resolveCountryName(rawCountry || isoCode, "en") || rawCountry;
const countryFa = const countryFa =
resolveCountryName(data.country || isoCode, "fa") || countryName;
resolveCountryName(rawCountry || isoCode, "fa") || countryName;
console.log( console.log(
`[GEO_AUTO_LOG] 🌍 Resolved Country: FA=${countryFa} | EN=${countryName} | Code=${isoCode} | City=${data.city || "None"} | IP=${data.ip || "None"}`, `[GEO_AUTO_LOG] 🌍 Resolved Country: FA=${countryFa} | EN=${countryName} | Code=${isoCode} | City=${data.city || "None"} | IP=${data.ip || "None"}`,

Loading…
Cancel
Save