import { Component, type MutableRefObject, type SyntheticEvent, useRef, } from "react"; import { runNativeCommand, isAndroid, type NativeArg } from "../utils"; export type RNMLEvent = { payload: PayloadType; type: string; }; let callbackIncrement = 0; type UseNativeBridge = { _nativeModuleName: string; _onAndroidCallback: (e: SyntheticEvent) => void; _callbackMap: MutableRefObject>; _preRefMapMethodQueue: MutableRefObject; _addAddAndroidCallback: ( id: string, resolve: (value: ReturnType) => void, reject: (error: Error) => void, ) => void; _removeAndroidCallback: (id: string) => void; _runPendingNativeCommands: ( nativeRef: RefType | null | undefined, ) => Promise; _runNativeCommand: ( methodName: string, nativeRef: RefType | undefined | null, args?: NativeArg[], ) => Promise; }; export const useNativeBridge: (moduleName: string) => UseNativeBridge = ( _nativeModuleName: string, ) => { const _callbackMap = useRef>(new Map()); const _preRefMapMethodQueue = useRef([]); const _addAddAndroidCallback = ( id: string, resolve: (value: ReturnType) => void, reject: (error: Error) => void, ): void => { _callbackMap.current.set(id, { resolve, reject }); }; const _removeAndroidCallback = (id: string): void => { _callbackMap.current.delete(id); }; const _onAndroidCallback = (e: SyntheticEvent): void => { const callbackID = e.nativeEvent.type; const callback = _callbackMap.current.get(callbackID); if (!callback) { return; } _callbackMap.current.delete(callbackID); const { payload } = e.nativeEvent; if (payload.error) { callback.reject.call(null, new Error(payload.error)); } else { callback.resolve.call(null, payload); } }; const _runPendingNativeCommands = async ( nativeRef: RefType | null | undefined, ): Promise => { if (nativeRef) { while (_preRefMapMethodQueue.current.length > 0) { const item = _preRefMapMethodQueue.current.pop(); if (item && item.method && item.resolver) { const res = await _runNativeCommand( item.method.name, nativeRef, item.method.args, ); item.resolver(res); } } } }; const _runNativeCommand = ( methodName: string, nativeRef: RefType | undefined | null, args: NativeArg[] = [], ): Promise => { if (!nativeRef) { return new Promise((resolve) => { _preRefMapMethodQueue.current.push({ method: { name: methodName, args }, resolver: resolve, }); }); } if (isAndroid()) { return new Promise((resolve, reject) => { callbackIncrement += 1; const callbackID = `${methodName}_${callbackIncrement}`; _addAddAndroidCallback(callbackID, resolve, reject); args.unshift(callbackID); runNativeCommand(_nativeModuleName, methodName, nativeRef, args); }); } return runNativeCommand(_nativeModuleName, methodName, nativeRef, args); }; return { _nativeModuleName, _onAndroidCallback, _callbackMap, _preRefMapMethodQueue, _addAddAndroidCallback, _removeAndroidCallback, _runPendingNativeCommands, _runNativeCommand, }; };