Skip to content

Commit

Permalink
fix: multiple linkBridge (#86)
Browse files Browse the repository at this point in the history
* fix: multiple linkBridge

* fix: by id

* fix: space

* fix: indent
  • Loading branch information
gronxb authored Dec 7, 2024
1 parent ca3051a commit c11d382
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 30 deletions.
12 changes: 8 additions & 4 deletions packages/react-native/src/createWebView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type {
Primitive,
} from "@webview-bridge/types";
import { createEvents } from "@webview-bridge/utils";
import React, {
import type React from "react";
import {
forwardRef,
useEffect,
useImperativeHandle,
Expand All @@ -19,12 +20,12 @@ import type { WebViewMessageEvent, WebViewProps } from "react-native-webview";
import WebView from "react-native-webview";

import {
handleBridge,
INJECT_BRIDGE_METHODS,
INJECT_BRIDGE_STATE,
SAFE_NATIVE_EMITTER_EMIT,
handleBridge,
} from "./integrations/bridge";
import { handleLog, INJECT_DEBUG, LogType } from "./integrations/console";
import { INJECT_DEBUG, type LogType, handleLog } from "./integrations/console";
import { handleRegisterWebMethod } from "./integrations/handleRegisterWebMethod";
import type { BridgeWebView } from "./types/webview";

Expand Down Expand Up @@ -190,6 +191,7 @@ export const createWebView = <
};
}, []);

// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
const initData = useMemo(() => {
const bridgeMethods = Object.entries(bridge.getState() ?? {})
.filter(([_, bridge]) => typeof bridge === "function")
Expand All @@ -203,6 +205,7 @@ export const createWebView = <
}, []);

useEffect(() => {
// lazy initialize the bridgeId
webviewRef.current?.injectJavaScript(
SAFE_NATIVE_EMITTER_EMIT("hydrate", initData),
);
Expand All @@ -216,7 +219,7 @@ export const createWebView = <
if (!webviewRef.current) {
return;
}
const { type, body } = JSON.parse(event.nativeEvent.data);
const { type, body, bridgeId } = JSON.parse(event.nativeEvent.data);

switch (type) {
case "log": {
Expand All @@ -235,6 +238,7 @@ export const createWebView = <
};

handleBridge({
bridgeId,
bridge,
method,
args,
Expand Down
46 changes: 38 additions & 8 deletions packages/react-native/src/integrations/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
Primitive,
} from "@webview-bridge/types";
import { equals, removeUndefinedKeys } from "@webview-bridge/utils";
import WebView from "react-native-webview";
import type WebView from "react-native-webview";

export type StoreCallback<T> = ({
get,
Expand Down Expand Up @@ -69,6 +69,7 @@ type HandleBridgeArgs<ArgType = unknown> = {
args?: ArgType[];
webview: WebView;
eventId: string;
bridgeId: string;
};

export const handleBridge = async ({
Expand All @@ -77,12 +78,15 @@ export const handleBridge = async ({
args,
webview,
eventId,
bridgeId,
}: HandleBridgeArgs) => {
const _bridge = bridge.getState();

const _method = _bridge[method];
const handleThrow = () => {
webview.injectJavaScript(SAFE_NATIVE_EMITTER_THROW(`${method}-${eventId}`));
webview.injectJavaScript(
SAFE_NATIVE_EMITTER_THROW_BY_BRIDGE_ID(bridgeId, `${method}-${eventId}`),
);
};
if (!(method in _bridge)) {
handleThrow();
Expand All @@ -96,7 +100,11 @@ export const handleBridge = async ({
const response = await _method?.(...(args ?? []));

webview.injectJavaScript(
SAFE_NATIVE_EMITTER_EMIT(`${method}-${eventId}`, response),
SAFE_NATIVE_EMITTER_EMIT_BY_BRIDGE_ID(
bridgeId,
`${method}-${eventId}`,
response,
),
);
} catch (error) {
handleThrow();
Expand All @@ -117,8 +125,27 @@ export const INJECT_BRIDGE_STATE = (
export const SAFE_NATIVE_EMITTER_EMIT = (eventName: string, data: unknown) => {
const dataString = JSON.stringify(data);
return `
if (window.nativeEmitter) {
window.nativeEmitter.emit('${eventName}', ${dataString});
if (window.nativeEmitter && Object.keys(window.nativeEmitter).length > 0) {
for (const [_, emitter] of Object.entries(window.nativeEmitter)) {
emitter.emit('${eventName}', ${dataString});
}
} else {
window.nativeBatchedEvents = window.nativeBatchedEvents || [];
window.nativeBatchedEvents.push(['${eventName}', ${dataString}]);
}
true;
`;
};

export const SAFE_NATIVE_EMITTER_EMIT_BY_BRIDGE_ID = (
bridgeId: string,
eventName: string,
data: unknown,
) => {
const dataString = JSON.stringify(data);
return `
if (window.nativeEmitter && window.nativeEmitter['${bridgeId}']) {
window.nativeEmitter['${bridgeId}'].emit('${eventName}', ${dataString});
} else {
window.nativeBatchedEvents = window.nativeBatchedEvents || [];
window.nativeBatchedEvents.push(['${eventName}', ${dataString}]);
Expand All @@ -127,9 +154,12 @@ true;
`;
};

export const SAFE_NATIVE_EMITTER_THROW = (eventName: string) => `
if (window.nativeEmitter) {
window.nativeEmitter.emit('${eventName}', {}, true);
export const SAFE_NATIVE_EMITTER_THROW_BY_BRIDGE_ID = (
bridgeId: string,
eventName: string,
) => `
if (window.nativeEmitter['${bridgeId}']) {
window.nativeEmitter['${bridgeId}'].emit('${eventName}', {}, true);
} else {
window.nativeBatchedEvents = window.nativeBatchedEvents || [];
window.nativeBatchedEvents.push(['${eventName}', {}, true]);
Expand Down
5 changes: 3 additions & 2 deletions packages/web/src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { DefaultEmitter } from "@webview-bridge/utils";

import { Primitive } from ".";
import type { Primitive } from "./types";

// biome-ignore lint/complexity/noUselessEmptyExport: <explanation>
export {};

declare global {
interface Window {
__bridgeMethods__?: string[];
__bridgeInitialState__?: Record<string, Primitive>;
nativeEmitter?: DefaultEmitter;
nativeEmitter?: Record<string, DefaultEmitter>;
nativeBatchedEvents?: [string, ...any][];
webEmitter?: DefaultEmitter;
ReactNativeWebView: {
Expand Down
16 changes: 9 additions & 7 deletions packages/web/src/internal/bridgeInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import type {
PrimitiveObject,
} from "@webview-bridge/types";
import {
type DefaultEmitter,
createRandomId,
createResolver,
DefaultEmitter,
timeout,
} from "@webview-bridge/utils";

import { NativeMethodError } from "../error";
import { LinkBridgeOptions } from "../linkBridge";
import { LinkBridge } from "../types";
import type { LinkBridgeOptions } from "../linkBridge";
import type { LinkBridge } from "../types";
import { createPromiseProxy } from "./createPromiseProxy";
import { linkBridgeStore } from "./linkBridgeStore";
import { mockStore } from "./mockStore";
Expand All @@ -26,10 +26,11 @@ export class BridgeInstance<
V extends ParserSchema<any> = ParserSchema<any>,
> {
constructor(
private _bridgeId: string,
private _options: LinkBridgeOptions<T, V>,

private _emitter: DefaultEmitter,
private _bridgeMethods: string[] = [],
private _bridgeMethods: string[],
public _nativeInitialState: PrimitiveObject,
) {
this._hydrate(_bridgeMethods, _nativeInitialState);
Expand Down Expand Up @@ -75,9 +76,11 @@ export class BridgeInstance<
? {
type,
body,
bridgeId: this._bridgeId,
}
: {
type,
bridgeId: this._bridgeId,
},
),
);
Expand Down Expand Up @@ -203,10 +206,9 @@ export class BridgeInstance<
writable: false,
});

return {
...acc,
return Object.assign(acc, {
[methodName]: nativeMethod,
};
});
},
initialBridge as LinkBridge<ExtractStore<T>, Omit<T, "setState">, V>,
);
Expand Down
22 changes: 13 additions & 9 deletions packages/web/src/linkBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import type {
ParserSchema,
PrimitiveObject,
} from "@webview-bridge/types";
import { createEvents } from "@webview-bridge/utils";
import { createEvents, createRandomId } from "@webview-bridge/utils";

import { MethodNotFoundError } from "./error";
import { BridgeInstance } from "./internal/bridgeInstance";
import { mockStore } from "./internal/mockStore";
import { LinkBridge } from "./types";
import type { LinkBridge } from "./types";

export interface LinkBridgeOptions<
T extends BridgeStore<T extends Bridge ? T : any>,
Expand Down Expand Up @@ -92,15 +92,19 @@ export const linkBridge = <
console.warn("[WebViewBridge] Not in a WebView environment");
}

const bridgeId = createRandomId();
const emitter = createEvents();
if (!window.nativeEmitter) {
window.nativeEmitter = emitter;
}

window.nativeEmitter = {
...(window.nativeEmitter || {}),
[bridgeId]: emitter,
};

const bridgeMethods = window.__bridgeMethods__ ?? [];
const nativeInitialState = window.__bridgeInitialState__ ?? {};

const instance = new BridgeInstance(
bridgeId,
options,
emitter,
bridgeMethods,
Expand Down Expand Up @@ -134,11 +138,11 @@ export const linkBridge = <
onFallback?.(methodName, args);
return Promise.reject(new MethodNotFoundError(methodName));
};
} else {
console.warn(
`[WebViewBridge] ${methodName} is not defined, using fallback.`,
);
}

console.warn(
`[WebViewBridge] ${methodName} is not defined, using fallback.`,
);
return () => Promise.resolve();
},
}) as LinkBridge<ExcludePrimitive<ExtractStore<T>>, Omit<T, "setState">, V>;
Expand Down

0 comments on commit c11d382

Please sign in to comment.