UNPKG

@habit.analytics/habit-smartlink-reactcomponent

Version:

A React component for Habit SmartLink integration.

93 lines (92 loc) 4.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useSmartlinkParentMessaging = void 0; var react_1 = require("react"); var types_1 = require("./types"); /** * @file useSmartlinkParentMessaging.ts * @description Parent-side hook for smartlink iframes. * * Responsibilities this hook owns: * - Deriving and enforcing pluginOrigin on all sends and receives * - Origin check on every incoming message (no route registry — origin only) * - Applying SMARTLINK_RESIZE to the iframe element (with optional onResize override) * - Emitting all messages to subscribers with an isValid flag * - Exposing named senders as conveniences * * Responsibilities this hook does NOT own: * - Deciding what to send when SMARTLINK_READY fires * - Handling step completion or cancellation * - Error handling beyond passing SMARTLINK_ERROR to subscribers * * All of the above are consumer responsibilities. */ var useSmartlinkParentMessaging = function (_a) { var pluginUrl = _a.pluginUrl, iframeRef = _a.iframeRef, onResize = _a.onResize; var pluginOrigin = pluginUrl ? new URL(pluginUrl).origin : null; /*------------------------------ SUBSCRIBERS STORE --------------------------------*/ var subscribersRef = (0, react_1.useRef)(new Set()); var subscribe = (0, react_1.useCallback)(function (handler) { subscribersRef.current.add(handler); return function () { subscribersRef.current.delete(handler); }; }, []); var subscribeTo = (0, react_1.useCallback)(function (type, handler) { return subscribe(function (message, event, isValid) { if (message.type !== type) return; handler(message, event, isValid); }); }, [subscribe]); var emitToSubscribers = (0, react_1.useCallback)(function (msg, event, isValid) { subscribersRef.current.forEach(function (fn) { return fn(msg, event, isValid); }); }, []); /*---------------------------------- SENDER ---------------------------------------*/ var sendMessage = (0, react_1.useCallback)(function (message) { var _a; if (!((_a = iframeRef.current) === null || _a === void 0 ? void 0 : _a.contentWindow) || !pluginOrigin) return; iframeRef.current.contentWindow.postMessage(message, pluginOrigin); }, [iframeRef, pluginOrigin]); var sendInit = (0, react_1.useCallback)(function (requestId) { sendMessage((0, types_1.createSmartlinkMessage)("SMARTLINK_INIT", undefined, requestId)); }, [sendMessage]); /*--------------------------------- RECEIVER --------------------------------------*/ (0, react_1.useEffect)(function () { var iframe = iframeRef.current; var handleMessage = function (event) { var _a; if (event.origin !== pluginOrigin) return; var data = event.data; if (!data || typeof data !== "object" || !("type" in data)) return; /* * Smartlink uses origin check only — no route registry revalidation. * isValid is true as long as the origin matches pluginOrigin. */ var isValid = true; if (data.type === "SMARTLINK_RESIZE") { var _b = (_a = data.payload) !== null && _a !== void 0 ? _a : {}, height = _b.height, width = _b.width; if (typeof height === "number" && iframe) { var requested = { height: height, width: width }; var override = onResize === null || onResize === void 0 ? void 0 : onResize(requested); var resolved = override !== null && override !== void 0 ? override : requested; iframe.style.height = "".concat(resolved.height, "px"); if (resolved.width !== undefined) { iframe.style.width = "".concat(resolved.width, "px"); } } } emitToSubscribers(data, event, isValid); }; window.addEventListener("message", handleMessage); return function () { window.removeEventListener("message", handleMessage); }; }, [pluginUrl, pluginOrigin, iframeRef, onResize, emitToSubscribers]); /*-------------------------------- EXPORT API -------------------------------------*/ return { sendMessage: sendMessage, sendInit: sendInit, subscribe: subscribe, subscribeTo: subscribeTo }; }; exports.useSmartlinkParentMessaging = useSmartlinkParentMessaging;