@kontextso/sdk-react-native 3.0.7-rc.3 → 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -56,10 +56,50 @@ function handleIframeMessage(handler, opts) {
56
56
 
57
57
  // src/formats/Format.tsx
58
58
  var import_sdk_react = require("@kontextso/sdk-react");
59
- var import_react = require("react");
59
+ var import_react2 = require("react");
60
60
  var import_react_native = require("react-native");
61
+
62
+ // src/frame-webview.tsx
63
+ var import_react = require("react");
61
64
  var import_react_native_webview = require("react-native-webview");
62
65
  var import_jsx_runtime = require("react/jsx-runtime");
66
+ var FrameWebView = (0, import_react.forwardRef)(
67
+ ({ iframeUrl, onMessage, style, onError, onLoad }, forwardedRef) => {
68
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
69
+ import_react_native_webview.WebView,
70
+ {
71
+ ref: forwardedRef,
72
+ source: {
73
+ uri: iframeUrl
74
+ },
75
+ onMessage,
76
+ style,
77
+ allowsInlineMediaPlayback: true,
78
+ mediaPlaybackRequiresUserAction: false,
79
+ javaScriptEnabled: true,
80
+ domStorageEnabled: true,
81
+ allowsFullscreenVideo: false,
82
+ originWhitelist: ["*"],
83
+ sharedCookiesEnabled: true,
84
+ thirdPartyCookiesEnabled: true,
85
+ injectedJavaScript: `
86
+ window.addEventListener("message", function(event) {
87
+ if (window.ReactNativeWebView && event.data) {
88
+ // ReactNativeWebView.postMessage only supports string data
89
+ window.ReactNativeWebView.postMessage(JSON.stringify(event.data));
90
+ }
91
+ }, false);
92
+ `,
93
+ onError,
94
+ onLoad
95
+ }
96
+ );
97
+ }
98
+ );
99
+ var frame_webview_default = FrameWebView;
100
+
101
+ // src/formats/Format.tsx
102
+ var import_jsx_runtime2 = require("react/jsx-runtime");
63
103
  var sendMessage = (webViewRef, type, code, data) => {
64
104
  const message = makeIframeMessage(type, {
65
105
  data,
@@ -71,24 +111,54 @@ var sendMessage = (webViewRef, type, code, data) => {
71
111
  }));
72
112
  `);
73
113
  };
74
- var getUrl = (code, messageId, bidId) => {
75
- const context = (0, import_react.useContext)(import_sdk_react.AdsContext);
76
- if (!context || !bidId) {
114
+ var getCachedContent = (context, bidId) => {
115
+ if (!bidId) {
77
116
  return null;
78
117
  }
79
- const adServerUrl = context?.adServerUrl;
80
- const params = new URLSearchParams({
81
- code,
82
- messageId,
83
- sdk: "sdk-react-native"
84
- });
85
- return `${adServerUrl}/api/frame/${bidId}?${params}`;
118
+ return context?.cachedContentRef?.current?.get(bidId) ?? null;
86
119
  };
87
120
  var Format = ({ code, messageId, wrapper, onEvent, ...otherParams }) => {
88
- const context = (0, import_react.useContext)(import_sdk_react.AdsContext);
121
+ const context = (0, import_react2.useContext)(import_sdk_react.AdsContext);
89
122
  const bid = (0, import_sdk_react.useBid)({ code, messageId });
90
- const iframeUrl = getUrl(code, messageId, bid?.bidId);
91
- const webViewRef = (0, import_react.useRef)(null);
123
+ const [height, setHeight] = (0, import_react2.useState)(0);
124
+ const cachedContent = getCachedContent(context, bid?.bidId);
125
+ const iframeUrl = (0, import_sdk_react.useIframeUrl)(bid, code, messageId, "sdk-react-native", otherParams.theme, cachedContent);
126
+ const modalUrl = iframeUrl.replace("/api/frame/", "/api/modal/");
127
+ const [showIframe, setShowIframe] = (0, import_react2.useState)(false);
128
+ const [iframeLoaded, setIframeLoaded] = (0, import_react2.useState)(false);
129
+ const [modalOpen, setModalOpen] = (0, import_react2.useState)(false);
130
+ const [modalShown, setModalShown] = (0, import_react2.useState)(false);
131
+ const [modalLoaded, setModalLoaded] = (0, import_react2.useState)(false);
132
+ const [containerStyles, setContainerStyles] = (0, import_react2.useState)({});
133
+ const [iframeStyles, setIframeStyles] = (0, import_react2.useState)({});
134
+ const containerRef = (0, import_react2.useRef)(null);
135
+ const webViewRef = (0, import_react2.useRef)(null);
136
+ const modalWebViewRef = (0, import_react2.useRef)(null);
137
+ const modalInitTimeoutRef = (0, import_react2.useRef)(null);
138
+ const isModalInitRef = (0, import_react2.useRef)(false);
139
+ const { height: windowHeight, width: windowWidth } = (0, import_react_native.useWindowDimensions)();
140
+ const keyboardHeightRef = (0, import_react2.useRef)(0);
141
+ const isAdViewVisible = showIframe && iframeLoaded;
142
+ const reset = () => {
143
+ setHeight(0);
144
+ setShowIframe(false);
145
+ setContainerStyles({});
146
+ setIframeStyles({});
147
+ setIframeLoaded(false);
148
+ resetModal();
149
+ context?.resetAll();
150
+ context?.captureError(new Error("Processing iframe error"));
151
+ };
152
+ const resetModal = () => {
153
+ if (modalInitTimeoutRef.current) {
154
+ clearTimeout(modalInitTimeoutRef.current);
155
+ modalInitTimeoutRef.current = null;
156
+ }
157
+ isModalInitRef.current = false;
158
+ setModalOpen(false);
159
+ setModalLoaded(false);
160
+ setModalShown(false);
161
+ };
92
162
  const debug = (name, data = {}) => {
93
163
  context?.onDebugEventInternal?.(name, {
94
164
  code,
@@ -96,36 +166,40 @@ var Format = ({ code, messageId, wrapper, onEvent, ...otherParams }) => {
96
166
  otherParams,
97
167
  bid,
98
168
  iframeUrl,
169
+ iframeLoaded,
170
+ showIframe,
171
+ height,
172
+ containerStyles,
173
+ iframeStyles,
99
174
  ...data
100
175
  });
101
176
  };
102
- debug("Format:updateState", {
103
- params: {
104
- messageId,
177
+ const debugModal = (name, data = {}) => {
178
+ context?.onDebugEventInternal?.(name, {
105
179
  code,
106
- otherParams
107
- }
108
- });
180
+ messageId,
181
+ otherParams,
182
+ bid,
183
+ modalUrl,
184
+ modalOpen,
185
+ modalShown,
186
+ modalLoaded,
187
+ ...data
188
+ });
189
+ };
190
+ debug("format-update-state");
109
191
  const onMessage = (event) => {
110
192
  try {
111
193
  const data = JSON.parse(event.nativeEvent.data);
112
- debug("Format:iframeMessage", {
113
- message: data,
114
- params: { data, messageId, code, otherParams }
194
+ debug("iframe-message", {
195
+ message: data
115
196
  });
116
197
  const messageHandler = handleIframeMessage(
117
198
  (message) => {
118
199
  switch (message.type) {
119
200
  case "init-iframe":
120
- debug("Format:iframePostMessage", {
121
- params: {
122
- code,
123
- messages: context?.messages,
124
- sdk: "sdk-react-native",
125
- otherParams,
126
- messageId
127
- }
128
- });
201
+ setIframeLoaded(true);
202
+ debug("iframe-post-message");
129
203
  sendMessage(webViewRef, "update-iframe", code, {
130
204
  messages: context?.messages,
131
205
  sdk: "sdk-react-native",
@@ -133,6 +207,46 @@ var Format = ({ code, messageId, wrapper, onEvent, ...otherParams }) => {
133
207
  messageId
134
208
  });
135
209
  break;
210
+ case "error-iframe":
211
+ reset();
212
+ break;
213
+ case "resize-iframe":
214
+ setHeight(message.data.height);
215
+ break;
216
+ case "click-iframe":
217
+ if (message.data.url) {
218
+ import_react_native.Linking.openURL(`${context?.adServerUrl}${message.data.url}`).catch(
219
+ (err) => console.error("error opening url", err)
220
+ );
221
+ }
222
+ context?.onAdClickInternal(message.data);
223
+ break;
224
+ case "view-iframe":
225
+ context?.onAdViewInternal(message.data);
226
+ break;
227
+ case "ad-done-iframe":
228
+ if (bid?.bidId && message.data.cachedContent) {
229
+ context?.cachedContentRef?.current?.set(bid.bidId, message.data.cachedContent);
230
+ }
231
+ break;
232
+ case "show-iframe":
233
+ setShowIframe(true);
234
+ break;
235
+ case "hide-iframe":
236
+ setShowIframe(false);
237
+ break;
238
+ case "set-styles-iframe":
239
+ setContainerStyles(message.data.containerStyles);
240
+ setIframeStyles(message.data.iframeStyles);
241
+ break;
242
+ case "open-component-iframe":
243
+ setModalOpen(true);
244
+ modalInitTimeoutRef.current = setTimeout(() => {
245
+ if (!isModalInitRef.current) {
246
+ resetModal();
247
+ }
248
+ }, message.data.timeout ?? 5e3);
249
+ break;
136
250
  case "event-iframe":
137
251
  onEvent?.(message.data);
138
252
  context?.onAdEventInternal(message.data);
@@ -145,145 +259,200 @@ var Format = ({ code, messageId, wrapper, onEvent, ...otherParams }) => {
145
259
  );
146
260
  messageHandler({ data });
147
261
  } catch (e) {
148
- debug("Format:iframeMessageError", {
149
- params: { error: e, messageId, code, otherParams },
262
+ debug("iframe-message-error", {
150
263
  error: e
151
264
  });
152
265
  console.error("error parsing message from webview", e);
266
+ reset();
153
267
  }
154
268
  };
155
- if (!context || !bid || !iframeUrl) {
156
- debug("Format:noContextOrBidOrIframeUrl", {
157
- params: {
158
- context,
159
- bid,
160
- iframeUrl,
161
- messageId,
162
- code,
163
- otherParams
164
- }
269
+ const onModalMessage = (event) => {
270
+ try {
271
+ const data = JSON.parse(event.nativeEvent.data);
272
+ debugModal("modal-iframe-message", {
273
+ message: data
274
+ });
275
+ const messageHandler = handleIframeMessage(
276
+ (message) => {
277
+ switch (message.type) {
278
+ case "close-component-iframe":
279
+ resetModal();
280
+ break;
281
+ case "init-component-iframe":
282
+ isModalInitRef.current = true;
283
+ if (modalInitTimeoutRef.current) {
284
+ clearTimeout(modalInitTimeoutRef.current);
285
+ modalInitTimeoutRef.current = null;
286
+ }
287
+ setModalShown(true);
288
+ break;
289
+ case "error-component-iframe":
290
+ case "error-iframe":
291
+ resetModal();
292
+ context?.captureError(new Error("Processing modal iframe error"));
293
+ break;
294
+ case "click-iframe":
295
+ if (message.data.url) {
296
+ import_react_native.Linking.openURL(`${context?.adServerUrl}${message.data.url}`).catch(
297
+ (err) => console.error("error opening url", err)
298
+ );
299
+ }
300
+ context?.onAdClickInternal(message.data);
301
+ break;
302
+ case "event-iframe":
303
+ onEvent?.(message.data);
304
+ context?.onAdEventInternal(message.data);
305
+ break;
306
+ }
307
+ },
308
+ {
309
+ code,
310
+ component: "modal"
311
+ }
312
+ );
313
+ messageHandler({ data });
314
+ } catch (e) {
315
+ debugModal("modal-iframe-message-error", {
316
+ error: e
317
+ });
318
+ console.error("error parsing message from webview", e);
319
+ resetModal();
320
+ }
321
+ };
322
+ const paramsString = (0, import_sdk_react.convertParamsToString)(otherParams);
323
+ (0, import_react2.useEffect)(() => {
324
+ if (!iframeLoaded || !context?.adServerUrl || !bid || !webViewRef.current) {
325
+ return;
326
+ }
327
+ debug("iframe-post-message");
328
+ sendMessage(webViewRef, "update-iframe", code, {
329
+ data: { otherParams },
330
+ code
331
+ });
332
+ }, [paramsString, iframeLoaded, context?.adServerUrl, bid, code]);
333
+ const checkIfInViewport = () => {
334
+ if (!containerRef.current) return;
335
+ containerRef.current.measureInWindow((containerX, containerY, containerWidth, containerHeight) => {
336
+ sendMessage(webViewRef, "update-dimensions-iframe", code, {
337
+ windowWidth,
338
+ windowHeight,
339
+ containerWidth,
340
+ containerHeight,
341
+ containerX,
342
+ containerY,
343
+ keyboardHeight: keyboardHeightRef.current
344
+ });
165
345
  });
346
+ };
347
+ (0, import_react2.useEffect)(() => {
348
+ if (!isAdViewVisible) return;
349
+ const interval = setInterval(() => {
350
+ checkIfInViewport();
351
+ }, 250);
352
+ return () => clearInterval(interval);
353
+ }, [isAdViewVisible]);
354
+ (0, import_react2.useEffect)(() => {
355
+ const showSubscription = import_react_native.Keyboard.addListener("keyboardDidShow", (e) => {
356
+ keyboardHeightRef.current = e?.endCoordinates?.height ?? 0;
357
+ });
358
+ const hideSubscription = import_react_native.Keyboard.addListener("keyboardDidHide", () => {
359
+ keyboardHeightRef.current = 0;
360
+ });
361
+ return () => {
362
+ showSubscription.remove();
363
+ hideSubscription.remove();
364
+ keyboardHeightRef.current = 0;
365
+ };
366
+ }, []);
367
+ if (!context || !bid || !iframeUrl) {
166
368
  return null;
167
369
  }
168
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
169
- import_react_native.View,
370
+ const inlineContent = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
371
+ frame_webview_default,
170
372
  {
373
+ ref: webViewRef,
374
+ iframeUrl,
375
+ onMessage,
171
376
  style: {
172
- height: 300,
377
+ height,
173
378
  width: "100%",
174
379
  backgroundColor: "transparent",
175
- borderWidth: 0
380
+ borderWidth: 0,
381
+ ...iframeStyles
176
382
  },
177
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
178
- import_react_native_webview.WebView,
383
+ onError: () => {
384
+ debug("iframe-error");
385
+ reset();
386
+ },
387
+ onLoad: () => {
388
+ debug("iframe-load");
389
+ }
390
+ }
391
+ );
392
+ const interstitialContent = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
393
+ import_react_native.Modal,
394
+ {
395
+ visible: modalOpen,
396
+ transparent: true,
397
+ onRequestClose: resetModal,
398
+ animationType: "slide",
399
+ statusBarTranslucent: true,
400
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
401
+ import_react_native.View,
179
402
  {
180
- ref: webViewRef,
181
- source: {
182
- uri: iframeUrl
183
- },
184
- onMessage,
185
403
  style: {
186
- height: 300,
187
- width: "100%",
188
- backgroundColor: "transparent",
189
- borderWidth: 0
404
+ flex: 1,
405
+ // Don't show the modal until the modal page is loaded and sends 'init-component-iframe' message back to SDK
406
+ ...modalShown ? { opacity: 1, pointerEvents: "auto" } : { opacity: 0, pointerEvents: "none" }
190
407
  },
191
- allowsInlineMediaPlayback: true,
192
- mediaPlaybackRequiresUserAction: false,
193
- javaScriptEnabled: true,
194
- domStorageEnabled: true,
195
- allowsFullscreenVideo: false,
196
- injectedJavaScript: `
197
- window.addEventListener("message", function(event) {
198
- if (window.ReactNativeWebView && event.data) {
199
- // ReactNativeWebView.postMessage only supports string data
200
- window.ReactNativeWebView.postMessage(JSON.stringify(event.data));
201
- }
202
- }, false);
203
- `,
204
- onLoadStart: () => {
205
- debug("Format:iframeLoadStart", {
206
- params: {
207
- messageId,
208
- code,
209
- otherParams
408
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
409
+ frame_webview_default,
410
+ {
411
+ ref: modalWebViewRef,
412
+ iframeUrl: modalUrl,
413
+ onMessage: onModalMessage,
414
+ style: {
415
+ backgroundColor: "transparent",
416
+ height: "100%",
417
+ width: "100%",
418
+ borderWidth: 0
419
+ },
420
+ onError: () => {
421
+ debug("modal-error");
422
+ resetModal();
423
+ },
424
+ onLoad: () => {
425
+ debug("modal-load");
426
+ setModalLoaded(true);
210
427
  }
211
- });
212
- },
213
- onError: () => {
214
- debug("Format:iframeError", {
215
- params: {
216
- messageId,
217
- code,
218
- otherParams
219
- }
220
- });
221
- },
222
- onLoad: () => {
223
- debug("Format:iframeLoad", {
224
- params: {
225
- messageId,
226
- code,
227
- otherParams
228
- }
229
- });
230
- },
231
- onLoadProgress: () => {
232
- debug("Format:iframeLoadProgress", {
233
- params: {
234
- messageId,
235
- code,
236
- otherParams
237
- }
238
- });
239
- },
240
- onHttpError: () => {
241
- debug("Format:iframeHttpError", {
242
- params: {
243
- messageId,
244
- code,
245
- otherParams
246
- }
247
- });
248
- },
249
- onRenderProcessGone: () => {
250
- debug("Format:iframeRenderProcessGone", {
251
- params: {
252
- messageId,
253
- code,
254
- otherParams
255
- }
256
- });
257
- },
258
- onNavigationStateChange: () => {
259
- debug("Format:iframeNavigationStateChange", {
260
- params: {
261
- messageId,
262
- code,
263
- otherParams
264
- }
265
- });
266
- },
267
- onContentProcessDidTerminate: () => {
268
- debug("Format:iframeContentProcessDidTerminate", {
269
- params: {
270
- messageId,
271
- code,
272
- otherParams
273
- }
274
- });
275
- }
428
+ }
429
+ )
276
430
  }
277
431
  )
278
432
  }
279
433
  );
434
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
435
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
436
+ import_react_native.View,
437
+ {
438
+ style: isAdViewVisible ? containerStyles : {
439
+ height: 0,
440
+ overflow: "hidden"
441
+ },
442
+ ref: containerRef,
443
+ children: wrapper ? wrapper(inlineContent) : inlineContent
444
+ }
445
+ ),
446
+ interstitialContent
447
+ ] });
280
448
  };
281
- var Format_default = Format;
449
+ var FormatWithErrorBoundary = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sdk_react.ErrorBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Format, { ...props }) });
450
+ var Format_default = FormatWithErrorBoundary;
282
451
 
283
452
  // src/formats/InlineAd.tsx
284
- var import_jsx_runtime2 = require("react/jsx-runtime");
453
+ var import_jsx_runtime3 = require("react/jsx-runtime");
285
454
  var InlineAd = ({ code, messageId, wrapper, ...props }) => {
286
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Format_default, { code, messageId, wrapper, ...props });
455
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Format_default, { code, messageId, wrapper, ...props });
287
456
  };
288
457
  var InlineAd_default = InlineAd;
289
458
 
@@ -294,14 +463,14 @@ var import_react_native3 = require("react-native");
294
463
  var import_react_native_device_info = __toESM(require("react-native-device-info"));
295
464
 
296
465
  // package.json
297
- var version = "3.0.7-rc.3";
466
+ var version = "3.0.7";
298
467
 
299
468
  // src/NativeRNKontext.ts
300
469
  var import_react_native2 = require("react-native");
301
470
  var NativeRNKontext_default = import_react_native2.TurboModuleRegistry.getEnforcing("RNKontext");
302
471
 
303
472
  // src/context/AdsProvider.tsx
304
- var import_jsx_runtime3 = require("react/jsx-runtime");
473
+ var import_jsx_runtime4 = require("react/jsx-runtime");
305
474
  ErrorUtils.setGlobalHandler((error, isFatal) => {
306
475
  if (!isFatal) {
307
476
  import_sdk_react2.log.warn(error);
@@ -395,7 +564,7 @@ var getSdk = async () => ({
395
564
  version
396
565
  });
397
566
  var AdsProvider = (props) => {
398
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sdk_react2.AdsProviderInternal, { ...props, getDevice, getSdk, getApp });
567
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sdk_react2.AdsProviderInternal, { ...props, getDevice, getSdk, getApp });
399
568
  };
400
569
  // Annotate the CommonJS export names for ESM import in node:
401
570
  0 && (module.exports = {