diff --git a/lib/modules/src/native.ts b/lib/modules/src/native.ts index 66516ab..5c28ac3 100644 --- a/lib/modules/src/native.ts +++ b/lib/modules/src/native.ts @@ -20,3 +20,71 @@ export function getNativeModule(name: string): T | null { throw new Error(`Unable to access native module: ${name}`) } + +const Bridge = getNativeModule<{ + getBBox(handle: number, options: object): object +}>('RNSVGRenderableModule')! + +const BridgePromise = getNativeModule<{ + readAsDataURL(map: object): Promise +}>('FileReaderModule')! + +/** + * HashMap conversion in native would make you lose the order of the arguments once the keys get more complicated than a single character + */ +function guardMaxArgs(args: any[]) { + if (args.length > 9) + throw new Error( + 'Passing more than 9 arguments to a native method is not supported.', + ) +} + +/** + * Calls a method on the native module and returns a promise that resolves with the result. + * + * @param name The name of the native method to call. + * @param args The arguments to pass to the native method. + * @returns A promise that resolves with the result of the native method call. + */ +export async function callMethod(name: string, args: any[]): Promise { + guardMaxArgs(args) + + const result = await BridgePromise.readAsDataURL({ + revenge: name, + ...args, + }) + + if ('error' in result) + throw new Error(`Call failed: ${result.error as string}`) + if ('result' in result) return result.result as T + + throw new Error( + 'Call failed: The module did not return a valid result. The native hook must have failed.', + ) +} + +/** + * Calls a method on the native module synchronously and returns the result. + * + * Only use synchronous methods when absolutely necessary, as they block JS execution until the native method returns. + * + * @param name The name of the native method to call. + * @param args The arguments to pass to the native method. + * @returns The result of the native method call. + */ +export function callMethodSync(name: string, args: any[]): T { + guardMaxArgs(args) + + try { + const result = Bridge.getBBox(0, { ...args, revenge: name }) + + if ('error' in result) throw result.error + if ('result' in result) return result.result as T + + throw 'The module did not return a valid result. The native hook must have failed.' + } catch (error) { + throw new Error( + `Call failed: ${error instanceof Error ? error.message : String(error)}`, + ) + } +}