import { findNodeHandle } from 'react-native'; export type NativeArg = | string | number | boolean | null | { [k: string]: NativeArg } | NativeArg[] | Function | GeoJSON.Geometry | undefined; type FunctionKeys = keyof { [K in keyof T as T[K] extends Function ? K : never]: T[K]; }; type RefType = React.Component; export class NativeCommands { module: Spec; preRefMethodQueue: Array<{ method: { name: FunctionKeys; args: NativeArg[] }; resolver: (value: unknown) => void; }>; nativeRef: RefType | undefined; constructor(module: Spec) { this.module = module; this.preRefMethodQueue = []; } async setNativeRef(nativeRef: RefType) { if (nativeRef) { this.nativeRef = nativeRef; while (this.preRefMethodQueue.length > 0) { const item = this.preRefMethodQueue.pop(); if (item && item.method && item.resolver) { const res = await this._call( item.method.name, nativeRef, item.method.args, ); item.resolver(res); } } } } call(name: FunctionKeys, args: NativeArg[]): Promise { if (this.nativeRef) { return this._call(name, this.nativeRef, args); } else { return new Promise((resolve) => { this.preRefMethodQueue.push({ method: { name, args }, resolver: resolve as (args: unknown) => void, }); }); } } _call( name: FunctionKeys, nativeRef: RefType, args: NativeArg[], ): Promise { const handle = findNodeHandle(nativeRef); if (handle) { return ( this.module[name] as ( arg0: number | null, ...args: NativeArg[] ) => Promise )(handle, ...args); } else { throw new Error( `Could not find handle for native ref ${module} when trying to invoke ${String( name, )}`, ); } } }