@kontextso/sdk-react-native 3.0.8-rc.0 → 3.1.0-rc.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/dist/index.js CHANGED
@@ -492,7 +492,7 @@ var import_react_native3 = require("react-native");
492
492
  var import_react_native_device_info = __toESM(require("react-native-device-info"));
493
493
 
494
494
  // package.json
495
- var version = "3.0.7";
495
+ var version = "3.1.0-rc.0";
496
496
 
497
497
  // src/NativeRNKontext.ts
498
498
  var import_react_native2 = require("react-native");
package/dist/index.mjs CHANGED
@@ -464,7 +464,7 @@ import { Appearance, Dimensions, PixelRatio, Platform } from "react-native";
464
464
  import DeviceInfo from "react-native-device-info";
465
465
 
466
466
  // package.json
467
- var version = "3.0.7";
467
+ var version = "3.1.0-rc.0";
468
468
 
469
469
  // src/NativeRNKontext.ts
470
470
  import { TurboModuleRegistry } from "react-native";
@@ -1,9 +1,15 @@
1
+ import Foundation
1
2
  import AVFoundation
3
+ import StoreKit
4
+ import UIKit
5
+ import React
2
6
 
3
7
  @objc(KontextSDK)
4
8
  public class KontextSDK: NSObject {
5
9
  private static let MINIMAL_VOLUME_THRESHOLD: Float = 0.0
6
10
 
11
+ // MARK: - Audio
12
+
7
13
  @objc
8
14
  public static func isSoundOn() -> NSNumber? {
9
15
  let session = AVAudioSession.sharedInstance()
@@ -23,4 +29,40 @@ public class KontextSDK: NSObject {
23
29
  return nil
24
30
  }
25
31
  }
32
+
33
+
34
+ // MARK: - SKOverlay
35
+
36
+ @objc
37
+ public static func presentSKOverlay(
38
+ _ appStoreId: String,
39
+ position: String,
40
+ dismissible: Bool,
41
+ resolver resolve: @escaping RCTPromiseResolveBlock,
42
+ rejecter reject: @escaping RCTPromiseRejectBlock
43
+ ) {
44
+ DispatchQueue.main.async {
45
+ do {
46
+ let ok = try SKOverlayManager.shared.present(
47
+ appStoreId: appStoreId,
48
+ position: position,
49
+ dismissible: dismissible
50
+ )
51
+ resolve(ok)
52
+ } catch {
53
+ reject("SK_OVERLAY_ERROR", error.localizedDescription, error)
54
+ }
55
+ }
56
+ }
57
+
58
+ @objc
59
+ public static func dismissSKOverlay(
60
+ _ resolve: @escaping RCTPromiseResolveBlock,
61
+ rejecter reject: @escaping RCTPromiseRejectBlock
62
+ ) {
63
+ DispatchQueue.main.async {
64
+ let ok = SKOverlayManager.shared.dismiss()
65
+ resolve(ok)
66
+ }
67
+ }
26
68
  }
package/ios/RNKontext.h CHANGED
@@ -1,3 +1,4 @@
1
+ #import <React/RCTBridgeModule.h>
1
2
  #import "RNKontext-Swift.h"
2
3
 
3
4
  #ifdef RCT_NEW_ARCH_ENABLED
package/ios/RNKontext.mm CHANGED
@@ -5,6 +5,7 @@
5
5
  RCT_EXPORT_MODULE()
6
6
 
7
7
  #ifdef RCT_NEW_ARCH_ENABLED
8
+
8
9
  - (void)isSoundOn:(RCTPromiseResolveBlock)resolve
9
10
  reject:(RCTPromiseRejectBlock)reject {
10
11
  NSNumber *isSoundOn = [KontextSDK isSoundOn];
@@ -16,13 +17,34 @@ RCT_EXPORT_MODULE()
16
17
  }
17
18
  }
18
19
 
20
+ // NEW: Present SKOverlay
21
+ - (void)presentSKOverlay:(NSString *)appStoreId
22
+ position:(NSString *)position
23
+ dismissible:(BOOL)dismissible
24
+ resolve:(RCTPromiseResolveBlock)resolve
25
+ reject:(RCTPromiseRejectBlock)reject {
26
+ [KontextSDK presentSKOverlay:appStoreId
27
+ position:position
28
+ dismissible:dismissible
29
+ resolver:resolve
30
+ rejecter:reject];
31
+ }
32
+
33
+ // NEW: Dismiss SKOverlay
34
+ - (void)dismissSKOverlay:(RCTPromiseResolveBlock)resolve
35
+ reject:(RCTPromiseRejectBlock)reject {
36
+ [KontextSDK dismissSKOverlay:resolve rejecter:reject];
37
+ }
38
+
19
39
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
20
40
  (const facebook::react::ObjCTurboModule::InitParams &)params {
21
41
  return std::make_shared<facebook::react::NativeRNKontextSpecJSI>(params);
22
42
  }
43
+
23
44
  #else
24
- RCT_EXPORT_METHOD(isSoundOn : (RCTPromiseResolveBlock)
25
- resolve rejecter : (RCTPromiseRejectBlock)reject) {
45
+
46
+ RCT_EXPORT_METHOD(isSoundOn : (RCTPromiseResolveBlock)resolve
47
+ rejecter : (RCTPromiseRejectBlock)reject) {
26
48
  NSNumber *isSoundOn = [KontextSDK isSoundOn];
27
49
 
28
50
  if (isSoundOn == nil) {
@@ -31,6 +53,26 @@ RCT_EXPORT_METHOD(isSoundOn : (RCTPromiseResolveBlock)
31
53
  resolve(isSoundOn);
32
54
  }
33
55
  }
56
+
57
+ // NEW: Present SKOverlay
58
+ RCT_EXPORT_METHOD(presentSKOverlay:(NSString *)appStoreId
59
+ position:(NSString *)position
60
+ dismissible:(BOOL)dismissible
61
+ resolver:(RCTPromiseResolveBlock)resolve
62
+ rejecter:(RCTPromiseRejectBlock)reject) {
63
+ [KontextSDK presentSKOverlay:appStoreId
64
+ position:position
65
+ dismissible:dismissible
66
+ resolver:resolve
67
+ rejecter:reject];
68
+ }
69
+
70
+ // NEW: Dismiss SKOverlay
71
+ RCT_EXPORT_METHOD(dismissSKOverlay:(RCTPromiseResolveBlock)resolve
72
+ rejecter:(RCTPromiseRejectBlock)reject) {
73
+ [KontextSDK dismissSKOverlay:resolve rejecter:reject];
74
+ }
75
+
34
76
  #endif
35
77
 
36
78
  @end
@@ -0,0 +1,49 @@
1
+ import Foundation
2
+ import StoreKit
3
+ import UIKit
4
+
5
+ final class SKOverlayManager {
6
+ static let shared = SKOverlayManager()
7
+
8
+ func present(appStoreId: String, position: String, dismissible: Bool) throws -> Bool {
9
+ guard #available(iOS 14.0, *), #available(iOS 13.0, *) else { return false }
10
+
11
+ guard let scene = UIApplication.shared.connectedScenes
12
+ .compactMap({ $0 as? UIWindowScene })
13
+ .first(where: { $0.activationState == .foregroundActive || $0.activationState == .foregroundInactive })
14
+ else {
15
+ throw NSError(domain: "SKOverlay", code: 1, userInfo: [
16
+ NSLocalizedDescriptionKey: "No active UIWindowScene found"
17
+ ])
18
+ }
19
+
20
+ dismiss(in: scene)
21
+
22
+ let pos: SKOverlay.Position = (position.lowercased() == "bottomraised") ? .bottomRaised : .bottom
23
+
24
+ let config = SKOverlay.AppConfiguration(appIdentifier: appStoreId, position: pos)
25
+ config.userDismissible = dismissible
26
+
27
+ let overlay = SKOverlay(configuration: config)
28
+ overlay.present(in: scene)
29
+ return true
30
+ }
31
+
32
+ func dismiss() -> Bool {
33
+ guard #available(iOS 14.0, *), #available(iOS 13.0, *) else { return false }
34
+
35
+ guard let scene = UIApplication.shared.connectedScenes
36
+ .compactMap({ $0 as? UIWindowScene })
37
+ .first(where: { $0.activationState == .foregroundActive || $0.activationState == .foregroundInactive })
38
+ else { return false }
39
+
40
+ dismiss(in: scene)
41
+ return true
42
+ }
43
+
44
+ @available(iOS 14.0, *)
45
+ @available(iOS 13.0, *)
46
+ private func dismiss(in scene: UIWindowScene) {
47
+ SKOverlay.dismiss(in: scene)
48
+ }
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextso/sdk-react-native",
3
- "version": "3.0.8-rc.0",
3
+ "version": "3.1.0-rc.0",
4
4
  "description": "Kontext SDK for React Native",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -3,6 +3,8 @@ import { TurboModuleRegistry } from 'react-native'
3
3
 
4
4
  export interface Spec extends TurboModule {
5
5
  isSoundOn(): Promise<boolean>
6
+ presentSKOverlay(appStoreId: string, position: string, dismissible: boolean): Promise<boolean>
7
+ dismissSKOverlay(): Promise<boolean>
6
8
  }
7
9
 
8
- export default TurboModuleRegistry.getEnforcing<Spec>('RNKontext')
10
+ export default TurboModuleRegistry.getEnforcing<Spec>("RNKontext");
@@ -10,7 +10,7 @@ import { fetch as fetchNetworkInfo, NetInfoStateType } from '@react-native-commu
10
10
  import { Appearance, Dimensions, PixelRatio, Platform } from 'react-native'
11
11
  import DeviceInfo, { type DeviceType } from 'react-native-device-info'
12
12
  import { version } from '../../package.json'
13
- import KontextSDK from '../NativeRNKontext'
13
+ import NativeRNKontext from '../NativeRNKontext'
14
14
 
15
15
  ErrorUtils.setGlobalHandler((error, isFatal) => {
16
16
  if (!isFatal) {
@@ -24,7 +24,7 @@ const getDevice = async (): Promise<DeviceConfig> => {
24
24
  try {
25
25
  const powerState = await DeviceInfo.getPowerState()
26
26
  const deviceType = DeviceInfo.getDeviceType() as DeviceType
27
- const soundOn = await KontextSDK.isSoundOn()
27
+ const soundOn = await NativeRNKontext.isSoundOn()
28
28
  const screen = Dimensions.get('screen')
29
29
  const networkInfo = await fetchNetworkInfo()
30
30
 
@@ -296,6 +296,13 @@ const Format = ({ code, messageId, wrapper, onEvent, ...otherParams }: FormatPro
296
296
  }
297
297
  }
298
298
 
299
+ /*
300
+ * Fix for Android issue in Saylo.
301
+ * When the iframe is loaded, the onLoad event is sometimes called two or more times.
302
+ * When these events fire too quickly, the init-iframe response from the server gets discarded.
303
+ * As a result, our SDK doesn’t send the update-iframe event back to the server, and the stream never starts.
304
+ * This fix will send the update-iframe event even if the init-iframe isn’t received.
305
+ */
299
306
  useEffect(() => {
300
307
  const interval = setInterval(() => {
301
308
  if (messageStatusRef.current === MessageStatus.None) {
@@ -0,0 +1,23 @@
1
+ import { Platform } from 'react-native'
2
+ import NativeRNKontext from '../NativeRNKontext';
3
+
4
+ export type SKOverlayPosition = 'bottom' | 'bottomRaised';
5
+
6
+ export async function presentSKOverlay(params: {
7
+ appStoreId: string;
8
+ position?: SKOverlayPosition;
9
+ dismissible?: boolean;
10
+ }) {
11
+ if (Platform.OS !== 'ios') {
12
+ return false
13
+ }
14
+ const { appStoreId, position = 'bottom', dismissible = true } = params
15
+ return NativeRNKontext.presentSKOverlay(appStoreId, position, dismissible)
16
+ }
17
+
18
+ export async function dismissSKOverlay() {
19
+ if (Platform.OS !== 'ios') {
20
+ return false
21
+ }
22
+ return NativeRNKontext.dismissSKOverlay()
23
+ }