diff --git a/src/components/ui/report-actions-sheet.tsx b/src/components/ui/report-actions-sheet.tsx
index 0a68792..d9575ba 100644
--- a/src/components/ui/report-actions-sheet.tsx
+++ b/src/components/ui/report-actions-sheet.tsx
@@ -36,28 +36,14 @@ export function ReportActionsSheet({ onClose }: ReportActionsSheetProps) {
};
// ✅ دکمه دریافت موقعیت مکانی
+ // پل اکنون از کانال واقعی HabibApp استفاده میکند، پس یکبار ارسال کافی است.
const handleGetLocation = () => {
- // روش قدیمی
- if (typeof window !== "undefined" && window.HabibApp) {
- window.HabibApp.postMessage(JSON.stringify({ action: "get_location" }));
- }
- // روش جدید با bridge
sendToFlutter("REQUEST_LOCATION");
console.log("📍 REQUEST_LOCATION ارسال شد");
};
// ✅ دکمه مشاور
const handleOpenConsultant = () => {
- // روش قدیمی
- if (typeof window !== "undefined" && window.HabibApp) {
- window.HabibApp.postMessage(
- JSON.stringify({
- action: "open_consultant_page",
- data: { consultant: "habib@gmail.com" },
- })
- );
- }
- // روش جدید با bridge
sendToFlutter("REQUEST_CONSULTANT", {
consultant: "habib@gmail.com",
});
@@ -225,10 +211,11 @@ export function ReportActionsSheet({ onClose }: ReportActionsSheetProps) {
💡 راهنما:
- • دکمه "WEB_READY" را بزنید
+ • WEB_READY هنگام باز شدن این شیت بهصورت خودکار از کانال
+ HabibApp ارسال میشود
- • در Flutter console لاگ را ببینید
-
• Flutter باید ایونت "INITIAL_CONFIG" بفرستد
+ • نقطهٔ سبز یعنی کانال HabibApp در دسترس است
+
• برای ارسال دستی، دکمه «ارسال WEB_READY» را بزنید
diff --git a/src/hooks/useFlutterBridge.ts b/src/hooks/useFlutterBridge.ts
index 42f7b4a..cce3a73 100644
--- a/src/hooks/useFlutterBridge.ts
+++ b/src/hooks/useFlutterBridge.ts
@@ -43,17 +43,31 @@ interface UseFlutterBridgeReturn {
// دریافت ایونتها
events: FlutterEvent[];
lastEvent: FlutterEvent | null;
-
+
// ارسال ایونتها
sendToFlutter: (type: WebEventType, payload?: any) => void;
-
+
// آماده بودن
isReady: boolean;
-
+
// لاگها
logs: string[];
}
+/**
+ * نگاشت ایونتهای وب به اکشنهای واقعی Flutter.
+ * اپ Flutter (najm) فقط کانال `HabibApp` را ثبت میکند و پیامهایی با
+ * فرمت `{ action, data }` را میفهمد. کانال `FlutterChannel` در Flutter
+ * وجود ندارد، بنابراین تنها مسیر معتبر ارسال، همین کانال است.
+ */
+const WEB_EVENT_TO_ACTION: Record = {
+ WEB_READY: "web_ready",
+ REQUEST_LOCATION: "get_location",
+ REQUEST_CONSULTANT: "open_consultant_page",
+ PAGE_LOADED: "page_loaded",
+ ERROR_OCCURRED: "error_occurred",
+};
+
export function useFlutterBridge(
options: UseFlutterBridgeOptions = {}
): UseFlutterBridgeReturn {
@@ -65,6 +79,7 @@ export function useFlutterBridge(
const [logs, setLogs] = useState([]);
const logsRef = useRef([]);
+ const hasSentReadyRef = useRef(false);
// تابع کمکی برای اضافه کردن لاگ
const addLog = (message: string, type: 'info' | 'success' | 'error' = 'info') => {
@@ -81,37 +96,41 @@ export function useFlutterBridge(
// تابع ارسال ایونت به Flutter
const sendToFlutter = (type: WebEventType, payload?: any) => {
- const event: WebEvent = {
- type,
- payload,
- timestamp: Date.now(),
- };
+ if (typeof window === 'undefined') return;
+
+ const action = WEB_EVENT_TO_ACTION[type];
+ let delivered = false;
try {
- // روش 1: postMessage (استاندارد)
+ // روش اصلی و واقعی: کانال HabibApp با فرمت { action, data }
+ // این تنها کانالی است که اپ Flutter ثبت کرده و به آن گوش میدهد.
+ if (window.HabibApp?.postMessage) {
+ const message =
+ payload !== undefined ? { action, data: payload } : { action };
+ window.HabibApp.postMessage(JSON.stringify(message));
+ delivered = true;
+ addLog(`📤 ارسال به Flutter (HabibApp): ${type} → ${action}`, 'success');
+ }
+
+ // مسیرهای fallback فقط برای محیط توسعه/iframe (در WebView واقعی Flutter وجود ندارند)
+ const event: WebEvent = { type, payload, timestamp: Date.now() };
+
if (window.parent && window.parent !== window) {
window.parent.postMessage(event, '*');
- addLog(`📤 ارسال به Flutter: ${type}`, 'success');
+ delivered = true;
}
- // روش 2: Flutter WebView interface (Android)
if ((window as any).FlutterChannel) {
(window as any).FlutterChannel.postMessage(JSON.stringify(event));
- addLog(`📤 ارسال به Flutter (Android): ${type}`, 'success');
+ delivered = true;
}
- // روش 3: iOS WebKit message handler
if ((window as any).webkit?.messageHandlers?.FlutterChannel) {
(window as any).webkit.messageHandlers.FlutterChannel.postMessage(event);
- addLog(`📤 ارسال به Flutter (iOS): ${type}`, 'success');
+ delivered = true;
}
- // اگر هیچ کدام موجود نبود
- if (
- (!window.parent || window.parent === window) &&
- !(window as any).FlutterChannel &&
- !(window as any).webkit?.messageHandlers?.FlutterChannel
- ) {
+ if (!delivered) {
addLog(`⚠️ محیط Flutter یافت نشد - در حالت توسعه`, 'info');
}
} catch (error) {
@@ -185,6 +204,77 @@ export function useFlutterBridge(
};
}, [onEvent, enableLogging]);
+ // تشخیص آمادگی واقعی + ارسال خودکار WEB_READY
+ // در WebView واقعی Flutter، شیء window.HabibApp توسط addJavaScriptChannel
+ // تزریق میشود؛ وجودش یعنی پل برقرار است. منتظر INITIAL_CONFIG نمیمانیم،
+ // چون Flutter چنین ایونتی نمیفرستد.
+ useEffect(() => {
+ if (typeof window === 'undefined') return;
+
+ let interval: ReturnType | undefined;
+ let timeout: ReturnType | undefined;
+
+ const markReadyAndAnnounce = () => {
+ if (!window.HabibApp?.postMessage) return false;
+
+ setIsReady(true);
+
+ if (!hasSentReadyRef.current) {
+ hasSentReadyRef.current = true;
+ sendToFlutter('WEB_READY', {
+ url: window.location.href,
+ userAgent: navigator.userAgent,
+ timestamp: Date.now(),
+ });
+ addLog('✅ WEB_READY بهصورت خودکار ارسال شد', 'success');
+ }
+
+ return true;
+ };
+
+ // اگر کانال هنوز تزریق نشده، کمی صبر میکنیم (تزریق ممکن است با تأخیر باشد)
+ if (!markReadyAndAnnounce()) {
+ interval = setInterval(() => {
+ if (markReadyAndAnnounce() && interval) clearInterval(interval);
+ }, 100);
+ timeout = setTimeout(() => {
+ if (interval) clearInterval(interval);
+ }, 5000);
+ }
+
+ return () => {
+ if (interval) clearInterval(interval);
+ if (timeout) clearTimeout(timeout);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ // گوشدادن به پاسخهای واقعی Flutter از مسیر window.onFlutterResponse.
+ // رسیدن هر پاسخ یعنی ارتباط دوطرفه برقرار است.
+ useEffect(() => {
+ if (typeof window === 'undefined') return;
+
+ const unsubscribe = window.addFlutterResponseListener?.((response) => {
+ setIsReady(true);
+
+ const flutterEvent: FlutterEvent = {
+ type: 'INITIAL_CONFIG',
+ payload: response,
+ timestamp: response?.data?.timestamp || Date.now(),
+ };
+
+ addLog(`📥 پاسخ از Flutter: ${response?.action}`, 'success');
+ setEvents((prev) => [...prev, flutterEvent].slice(-20));
+ setLastEvent(flutterEvent);
+ onEvent?.(flutterEvent);
+ });
+
+ return () => {
+ unsubscribe?.();
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [onEvent]);
+
return {
events,
lastEvent,