@kontextso/sdk-react-native 2.4.0 → 2.5.0

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/README.md CHANGED
@@ -2,86 +2,4 @@
2
2
 
3
3
  A lightweight React Native SDK for integrating Kontext's AI-powered ads into your iOS and Android apps.
4
4
 
5
- ---
6
-
7
- ## Quick Setup
8
-
9
- ### Prerequisites
10
-
11
- To get started, you will need to set up a publisher account to get a [`publisherToken` and `code`](https://docs.kontext.so/publishers#getting-started-is-easy).
12
-
13
- ### 1. Installation
14
-
15
- ```bash
16
- # Install the SDK
17
- npm install @kontextso/sdk-react-native
18
-
19
- # Install required dependencies
20
- npm install react-native-device-info
21
- npm install react-native-webview
22
- ```
23
-
24
- ### 2. Initialize AdsProvider
25
-
26
- Set up the ad provider early in your component tree:
27
-
28
- ```tsx
29
- import React, { useState } from 'react';
30
- import { AdsProvider } from '@kontextso/sdk-react-native';
31
-
32
- interface Message {
33
- id: string;
34
- role: 'user' | 'assistant';
35
- content: string;
36
- createdAt: Date;
37
- }
38
-
39
- function App() {
40
- const [messages, setMessages] = useState<Message[]>([]);
41
-
42
- return (
43
- <AdsProvider
44
- publisherToken="<your-publisher-token>"
45
- messages={messages}
46
- userId="<unique-user-id>"
47
- conversationId="<conversation-id>"
48
- enabledPlacementCodes={["<placement-code>"]}
49
- character={{
50
- name: "<name>",
51
- avatarUrl: "<url>",
52
- isNsfw: <boolean>,
53
- id: "<character-id>",
54
- greeting: "<greeting text>",
55
- persona: "<persona description>",
56
- tags: ["tag1", "tag2"],
57
- }}
58
- >
59
- {/* Rest of your app components */}
60
- </AdsProvider>
61
- );
62
- }
63
- ```
64
-
65
- ### 3. Display Your First Ad
66
-
67
- Insert ads inline within your UI, tied to messages:
68
-
69
- ```tsx
70
- function MessageList({ messages }: { messages: Message[] }) {
71
- return (
72
- <View>
73
- {messages.map((m) => (
74
- <View key={m.id}>
75
- {/* Your message component */}
76
- <InlineAd code="<placement-code>" messageId={m.id} />
77
- </View>
78
- ))}
79
- </View>
80
- );
81
- }
82
- ```
83
-
84
- ## Additional Resources
85
-
86
- * Explore a working [Demo Project featuring SDK integration](https://github.com/kontextso/sdk-react-native-demo).
87
- * Full documentation: [Kontext React Native SDK](https://docs.kontext.so/sdk/react-native).
5
+ 📚 Full documentation: [Kontext React Native SDK](https://docs.kontext.so/sdk/react-native).
package/dist/index.js CHANGED
@@ -126,6 +126,7 @@ var Format = ({ code, messageId, wrapper, onEvent, ...otherParams }) => {
126
126
  const modalInitTimeoutRef = (0, import_react2.useRef)(null);
127
127
  const isModalInitRef = (0, import_react2.useRef)(false);
128
128
  const { height: windowHeight, width: windowWidth } = (0, import_react_native.useWindowDimensions)();
129
+ const isAdViewVisible = showIframe && iframeLoaded;
129
130
  const reset = () => {
130
131
  setHeight(0);
131
132
  setShowIframe(false);
@@ -326,93 +327,93 @@ var Format = ({ code, messageId, wrapper, onEvent, ...otherParams }) => {
326
327
  });
327
328
  };
328
329
  (0, import_react2.useEffect)(() => {
330
+ if (!isAdViewVisible) return;
329
331
  const interval = setInterval(() => {
330
332
  checkIfInViewport();
331
333
  }, 250);
332
334
  return () => clearInterval(interval);
333
- }, []);
334
- if (!context || !bid || !iframeUrl || context.isDisabled) {
335
+ }, [isAdViewVisible]);
336
+ if (!context || !bid || !iframeUrl) {
335
337
  return null;
336
338
  }
337
- const getWidth = () => {
338
- if (showIframe && iframeLoaded) {
339
- return "100%";
339
+ const inlineContent = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
340
+ frame_webview_default,
341
+ {
342
+ ref: webViewRef,
343
+ iframeUrl,
344
+ onMessage,
345
+ style: {
346
+ height,
347
+ width: "100%",
348
+ backgroundColor: "transparent",
349
+ borderWidth: 0,
350
+ ...iframeStyles
351
+ },
352
+ onError: () => {
353
+ debug("iframe-error");
354
+ reset();
355
+ },
356
+ onLoad: () => {
357
+ debug("iframe-load");
358
+ }
340
359
  }
341
- return 0;
342
- };
343
- const getHeight = () => {
344
- if (showIframe && iframeLoaded) {
345
- return height;
360
+ );
361
+ const interstitialContent = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
362
+ import_react_native.Modal,
363
+ {
364
+ visible: modalOpen,
365
+ transparent: true,
366
+ onRequestClose: resetModal,
367
+ animationType: "slide",
368
+ statusBarTranslucent: true,
369
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
370
+ import_react_native.View,
371
+ {
372
+ style: {
373
+ flex: 1,
374
+ // Don't show the modal until the modal page is loaded and sends 'init-component-iframe' message back to SDK
375
+ ...modalShown ? { opacity: 1, pointerEvents: "auto" } : { opacity: 0, pointerEvents: "none" }
376
+ },
377
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
378
+ frame_webview_default,
379
+ {
380
+ ref: modalWebViewRef,
381
+ iframeUrl: modalUrl,
382
+ onMessage: onModalMessage,
383
+ style: {
384
+ backgroundColor: "transparent",
385
+ height: "100%",
386
+ width: "100%",
387
+ borderWidth: 0
388
+ },
389
+ onError: () => {
390
+ debug("modal-error");
391
+ resetModal();
392
+ },
393
+ onLoad: () => {
394
+ debug("modal-load");
395
+ setModalLoaded(true);
396
+ }
397
+ }
398
+ )
399
+ }
400
+ )
346
401
  }
347
- return 0;
348
- };
349
- const content = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
402
+ );
403
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
350
404
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
351
- import_react_native.Modal,
405
+ import_react_native.View,
352
406
  {
353
- visible: modalOpen,
354
- transparent: true,
355
- onRequestClose: resetModal,
356
- animationType: "slide",
357
- statusBarTranslucent: true,
358
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
359
- import_react_native.View,
360
- {
361
- style: {
362
- flex: 1,
363
- // Don't show the modal until the modal page is loaded and sends 'init-component-iframe' message back to SDK
364
- ...modalShown ? { opacity: 1, pointerEvents: "auto" } : { opacity: 0, pointerEvents: "none" }
365
- },
366
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
367
- frame_webview_default,
368
- {
369
- ref: modalWebViewRef,
370
- iframeUrl: modalUrl,
371
- onMessage: onModalMessage,
372
- style: {
373
- backgroundColor: "transparent",
374
- height: "100%",
375
- width: "100%",
376
- borderWidth: 0
377
- },
378
- onError: () => {
379
- debug("modal-error");
380
- resetModal();
381
- },
382
- onLoad: () => {
383
- debug("modal-load");
384
- setModalLoaded(true);
385
- }
386
- }
387
- )
388
- }
389
- )
390
- }
391
- ),
392
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native.View, { style: containerStyles, ref: containerRef, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
393
- frame_webview_default,
394
- {
395
- ref: webViewRef,
396
- iframeUrl,
397
- onMessage,
398
- style: {
399
- height: getHeight(),
400
- width: getWidth(),
401
- backgroundColor: "transparent",
402
- borderWidth: 0,
403
- ...iframeStyles
404
- },
405
- onError: () => {
406
- debug("iframe-error");
407
- reset();
407
+ style: isAdViewVisible ? containerStyles : {
408
+ height: 0,
409
+ overflow: "hidden"
408
410
  },
409
- onLoad: () => {
410
- debug("iframe-load");
411
- }
411
+ ref: containerRef,
412
+ children: wrapper ? wrapper(inlineContent) : inlineContent
412
413
  }
413
- ) })
414
+ ),
415
+ interstitialContent
414
416
  ] });
415
- return wrapper ? wrapper(content) : content;
416
417
  };
417
418
  var FormatWithErrorBoundary = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sdk_react.ErrorBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Format, { ...props }) });
418
419
  var Format_default = FormatWithErrorBoundary;
@@ -434,7 +435,7 @@ var import_react_native2 = require("react-native");
434
435
  var NativeRNKontext_default = import_react_native2.TurboModuleRegistry.getEnforcing("RNKontext");
435
436
 
436
437
  // package.json
437
- var version = "2.4.0";
438
+ var version = "2.5.0";
438
439
 
439
440
  // src/context/AdsProvider.tsx
440
441
  var import_jsx_runtime4 = require("react/jsx-runtime");
package/dist/index.mjs CHANGED
@@ -95,6 +95,7 @@ var Format = ({ code, messageId, wrapper, onEvent, ...otherParams }) => {
95
95
  const modalInitTimeoutRef = useRef(null);
96
96
  const isModalInitRef = useRef(false);
97
97
  const { height: windowHeight, width: windowWidth } = useWindowDimensions();
98
+ const isAdViewVisible = showIframe && iframeLoaded;
98
99
  const reset = () => {
99
100
  setHeight(0);
100
101
  setShowIframe(false);
@@ -295,93 +296,93 @@ var Format = ({ code, messageId, wrapper, onEvent, ...otherParams }) => {
295
296
  });
296
297
  };
297
298
  useEffect(() => {
299
+ if (!isAdViewVisible) return;
298
300
  const interval = setInterval(() => {
299
301
  checkIfInViewport();
300
302
  }, 250);
301
303
  return () => clearInterval(interval);
302
- }, []);
303
- if (!context || !bid || !iframeUrl || context.isDisabled) {
304
+ }, [isAdViewVisible]);
305
+ if (!context || !bid || !iframeUrl) {
304
306
  return null;
305
307
  }
306
- const getWidth = () => {
307
- if (showIframe && iframeLoaded) {
308
- return "100%";
308
+ const inlineContent = /* @__PURE__ */ jsx2(
309
+ frame_webview_default,
310
+ {
311
+ ref: webViewRef,
312
+ iframeUrl,
313
+ onMessage,
314
+ style: {
315
+ height,
316
+ width: "100%",
317
+ backgroundColor: "transparent",
318
+ borderWidth: 0,
319
+ ...iframeStyles
320
+ },
321
+ onError: () => {
322
+ debug("iframe-error");
323
+ reset();
324
+ },
325
+ onLoad: () => {
326
+ debug("iframe-load");
327
+ }
309
328
  }
310
- return 0;
311
- };
312
- const getHeight = () => {
313
- if (showIframe && iframeLoaded) {
314
- return height;
329
+ );
330
+ const interstitialContent = /* @__PURE__ */ jsx2(
331
+ Modal,
332
+ {
333
+ visible: modalOpen,
334
+ transparent: true,
335
+ onRequestClose: resetModal,
336
+ animationType: "slide",
337
+ statusBarTranslucent: true,
338
+ children: /* @__PURE__ */ jsx2(
339
+ View,
340
+ {
341
+ style: {
342
+ flex: 1,
343
+ // Don't show the modal until the modal page is loaded and sends 'init-component-iframe' message back to SDK
344
+ ...modalShown ? { opacity: 1, pointerEvents: "auto" } : { opacity: 0, pointerEvents: "none" }
345
+ },
346
+ children: /* @__PURE__ */ jsx2(
347
+ frame_webview_default,
348
+ {
349
+ ref: modalWebViewRef,
350
+ iframeUrl: modalUrl,
351
+ onMessage: onModalMessage,
352
+ style: {
353
+ backgroundColor: "transparent",
354
+ height: "100%",
355
+ width: "100%",
356
+ borderWidth: 0
357
+ },
358
+ onError: () => {
359
+ debug("modal-error");
360
+ resetModal();
361
+ },
362
+ onLoad: () => {
363
+ debug("modal-load");
364
+ setModalLoaded(true);
365
+ }
366
+ }
367
+ )
368
+ }
369
+ )
315
370
  }
316
- return 0;
317
- };
318
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
371
+ );
372
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
319
373
  /* @__PURE__ */ jsx2(
320
- Modal,
374
+ View,
321
375
  {
322
- visible: modalOpen,
323
- transparent: true,
324
- onRequestClose: resetModal,
325
- animationType: "slide",
326
- statusBarTranslucent: true,
327
- children: /* @__PURE__ */ jsx2(
328
- View,
329
- {
330
- style: {
331
- flex: 1,
332
- // Don't show the modal until the modal page is loaded and sends 'init-component-iframe' message back to SDK
333
- ...modalShown ? { opacity: 1, pointerEvents: "auto" } : { opacity: 0, pointerEvents: "none" }
334
- },
335
- children: /* @__PURE__ */ jsx2(
336
- frame_webview_default,
337
- {
338
- ref: modalWebViewRef,
339
- iframeUrl: modalUrl,
340
- onMessage: onModalMessage,
341
- style: {
342
- backgroundColor: "transparent",
343
- height: "100%",
344
- width: "100%",
345
- borderWidth: 0
346
- },
347
- onError: () => {
348
- debug("modal-error");
349
- resetModal();
350
- },
351
- onLoad: () => {
352
- debug("modal-load");
353
- setModalLoaded(true);
354
- }
355
- }
356
- )
357
- }
358
- )
359
- }
360
- ),
361
- /* @__PURE__ */ jsx2(View, { style: containerStyles, ref: containerRef, children: /* @__PURE__ */ jsx2(
362
- frame_webview_default,
363
- {
364
- ref: webViewRef,
365
- iframeUrl,
366
- onMessage,
367
- style: {
368
- height: getHeight(),
369
- width: getWidth(),
370
- backgroundColor: "transparent",
371
- borderWidth: 0,
372
- ...iframeStyles
373
- },
374
- onError: () => {
375
- debug("iframe-error");
376
- reset();
376
+ style: isAdViewVisible ? containerStyles : {
377
+ height: 0,
378
+ overflow: "hidden"
377
379
  },
378
- onLoad: () => {
379
- debug("iframe-load");
380
- }
380
+ ref: containerRef,
381
+ children: wrapper ? wrapper(inlineContent) : inlineContent
381
382
  }
382
- ) })
383
+ ),
384
+ interstitialContent
383
385
  ] });
384
- return wrapper ? wrapper(content) : content;
385
386
  };
386
387
  var FormatWithErrorBoundary = (props) => /* @__PURE__ */ jsx2(ErrorBoundary, { children: /* @__PURE__ */ jsx2(Format, { ...props }) });
387
388
  var Format_default = FormatWithErrorBoundary;
@@ -403,7 +404,7 @@ import { TurboModuleRegistry } from "react-native";
403
404
  var NativeRNKontext_default = TurboModuleRegistry.getEnforcing("RNKontext");
404
405
 
405
406
  // package.json
406
- var version = "2.4.0";
407
+ var version = "2.5.0";
407
408
 
408
409
  // src/context/AdsProvider.tsx
409
410
  import { jsx as jsx4 } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextso/sdk-react-native",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Kontext SDK for React Native",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -55,7 +55,7 @@
55
55
  "react-native-webview": "^13.10.0"
56
56
  },
57
57
  "dependencies": {
58
- "@kontextso/sdk-react": "^2.1.0"
58
+ "@kontextso/sdk-react": "^2.2.1"
59
59
  },
60
60
  "files": [
61
61
  "dist/*",
@@ -63,6 +63,8 @@ const Format = ({ code, messageId, wrapper, onEvent, ...otherParams }: FormatPro
63
63
 
64
64
  const { height: windowHeight, width: windowWidth } = useWindowDimensions()
65
65
 
66
+ const isAdViewVisible = showIframe && iframeLoaded
67
+
66
68
  const reset = () => {
67
69
  setHeight(0)
68
70
  setShowIframe(false)
@@ -297,94 +299,98 @@ const Format = ({ code, messageId, wrapper, onEvent, ...otherParams }: FormatPro
297
299
  }
298
300
 
299
301
  useEffect(() => {
302
+ if (!isAdViewVisible) return
303
+
300
304
  const interval = setInterval(() => {
301
305
  checkIfInViewport()
302
306
  }, 250)
303
307
 
304
308
  return () => clearInterval(interval)
305
- }, [])
309
+ }, [isAdViewVisible])
306
310
 
307
- if (!context || !bid || !iframeUrl || context.isDisabled) {
311
+ if (!context || !bid || !iframeUrl) {
308
312
  return null
309
313
  }
310
314
 
311
- const getWidth = () => {
312
- if (showIframe && iframeLoaded) {
313
- return '100%'
314
- }
315
- return 0
316
- }
317
-
318
- const getHeight = () => {
319
- if (showIframe && iframeLoaded) {
320
- return height
321
- }
322
- return 0
323
- }
315
+ const inlineContent = (
316
+ <FrameWebView
317
+ ref={webViewRef}
318
+ iframeUrl={iframeUrl}
319
+ onMessage={onMessage}
320
+ style={{
321
+ height,
322
+ width: '100%',
323
+ backgroundColor: 'transparent',
324
+ borderWidth: 0,
325
+ ...iframeStyles,
326
+ }}
327
+ onError={() => {
328
+ debug('iframe-error')
329
+ reset()
330
+ }}
331
+ onLoad={() => {
332
+ debug('iframe-load')
333
+ }}
334
+ />
335
+ )
324
336
 
325
- const content = (
326
- <>
327
- <Modal
328
- visible={modalOpen}
329
- transparent={true}
330
- onRequestClose={resetModal}
331
- animationType="slide"
332
- statusBarTranslucent={true}
337
+ const interstitialContent = (
338
+ <Modal
339
+ visible={modalOpen}
340
+ transparent={true}
341
+ onRequestClose={resetModal}
342
+ animationType="slide"
343
+ statusBarTranslucent={true}
344
+ >
345
+ <View
346
+ style={{
347
+ flex: 1,
348
+ // Don't show the modal until the modal page is loaded and sends 'init-component-iframe' message back to SDK
349
+ ...(modalShown ? { opacity: 1, pointerEvents: 'auto' } : { opacity: 0, pointerEvents: 'none' }),
350
+ }}
333
351
  >
334
- <View
335
- style={{
336
- flex: 1,
337
- // Don't show the modal until the modal page is loaded and sends 'init-component-iframe' message back to SDK
338
- ...(modalShown ? { opacity: 1, pointerEvents: 'auto' } : { opacity: 0, pointerEvents: 'none' }),
339
- }}
340
- >
341
- <FrameWebView
342
- ref={modalWebViewRef}
343
- iframeUrl={modalUrl}
344
- onMessage={onModalMessage}
345
- style={{
346
- backgroundColor: 'transparent',
347
- height: '100%',
348
- width: '100%',
349
- borderWidth: 0,
350
- }}
351
- onError={() => {
352
- debug('modal-error')
353
- resetModal()
354
- }}
355
- onLoad={() => {
356
- debug('modal-load')
357
- setModalLoaded(true)
358
- }}
359
- />
360
- </View>
361
- </Modal>
362
-
363
- <View style={containerStyles} ref={containerRef}>
364
352
  <FrameWebView
365
- ref={webViewRef}
366
- iframeUrl={iframeUrl}
367
- onMessage={onMessage}
353
+ ref={modalWebViewRef}
354
+ iframeUrl={modalUrl}
355
+ onMessage={onModalMessage}
368
356
  style={{
369
- height: getHeight(),
370
- width: getWidth(),
371
357
  backgroundColor: 'transparent',
358
+ height: '100%',
359
+ width: '100%',
372
360
  borderWidth: 0,
373
- ...iframeStyles,
374
361
  }}
375
362
  onError={() => {
376
- debug('iframe-error')
377
- reset()
363
+ debug('modal-error')
364
+ resetModal()
378
365
  }}
379
366
  onLoad={() => {
380
- debug('iframe-load')
367
+ debug('modal-load')
368
+ setModalLoaded(true)
381
369
  }}
382
370
  />
383
371
  </View>
384
- </>
372
+ </Modal>
385
373
  )
386
374
 
387
- return wrapper ? wrapper(content) : content
375
+ return (
376
+ <>
377
+ <View
378
+ style={
379
+ isAdViewVisible
380
+ ? containerStyles
381
+ : {
382
+ height: 0,
383
+ overflow: 'hidden',
384
+ }
385
+ }
386
+ ref={containerRef}
387
+ >
388
+ {wrapper ? wrapper(inlineContent) : inlineContent}
389
+ </View>
390
+
391
+ {interstitialContent}
392
+ </>
393
+ )
388
394
  }
389
395
 
390
396
  const FormatWithErrorBoundary = (props: FormatProps) => (