@kontextso/sdk-react-native 3.0.8 → 3.1.0-rc.1
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 +1 -1
- package/dist/index.mjs +1 -1
- package/ios/KontextSDK.swift +42 -0
- package/ios/RNKontext.h +1 -0
- package/ios/RNKontext.mm +44 -2
- package/ios/SkOverlayManager.swift +49 -0
- package/package.json +2 -2
- package/src/NativeRNKontext.ts +3 -1
- package/src/context/AdsProvider.tsx +2 -2
- package/src/formats/Format.tsx +34 -1
- package/src/services/SkOverlay.ts +42 -0
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.
|
|
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.
|
|
467
|
+
var version = "3.1.0-rc.0";
|
|
468
468
|
|
|
469
469
|
// src/NativeRNKontext.ts
|
|
470
470
|
import { TurboModuleRegistry } from "react-native";
|
package/ios/KontextSDK.swift
CHANGED
|
@@ -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
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
|
-
|
|
25
|
-
|
|
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.
|
|
3
|
+
"version": "3.1.0-rc.1",
|
|
4
4
|
"description": "Kontext SDK for React Native",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"format": "biome format --write ."
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@kontextso/sdk-common": "^1.0.
|
|
23
|
+
"@kontextso/sdk-common": "^1.0.2",
|
|
24
24
|
"@kontextso/typescript-config": "*",
|
|
25
25
|
"@react-native-community/netinfo": "11.3.1",
|
|
26
26
|
"@testing-library/dom": "^10.4.0",
|
package/src/NativeRNKontext.ts
CHANGED
|
@@ -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>(
|
|
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
|
|
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
|
|
27
|
+
const soundOn = await NativeRNKontext.isSoundOn()
|
|
28
28
|
const screen = Dimensions.get('screen')
|
|
29
29
|
const networkInfo = await fetchNetworkInfo()
|
|
30
30
|
|
package/src/formats/Format.tsx
CHANGED
|
@@ -18,10 +18,11 @@ import { useContext, useEffect, useRef, useState } from 'react'
|
|
|
18
18
|
import { Keyboard, Linking, Modal, useWindowDimensions, View } from 'react-native'
|
|
19
19
|
import type { WebView, WebViewMessageEvent } from 'react-native-webview'
|
|
20
20
|
import FrameWebView from '../frame-webview'
|
|
21
|
+
import NativeRNKontext from '../NativeRNKontext'
|
|
21
22
|
|
|
22
23
|
const sendMessage = (
|
|
23
24
|
webViewRef: React.RefObject<WebView>,
|
|
24
|
-
type: Extract<IframeMessageType, 'update-iframe' | 'update-dimensions-iframe'>,
|
|
25
|
+
type: Extract<IframeMessageType, 'update-iframe' | 'update-dimensions-iframe' | 'update-skoverlay-iframe'>,
|
|
25
26
|
code: string,
|
|
26
27
|
data: any
|
|
27
28
|
) => {
|
|
@@ -90,6 +91,7 @@ const Format = ({ code, messageId, wrapper, onEvent, ...otherParams }: FormatPro
|
|
|
90
91
|
setContainerStyles({})
|
|
91
92
|
setIframeStyles({})
|
|
92
93
|
setIframeLoaded(false)
|
|
94
|
+
closeSkOverlay()
|
|
93
95
|
resetModal()
|
|
94
96
|
context?.resetAll()
|
|
95
97
|
context?.captureError(new Error('Processing iframe error'))
|
|
@@ -137,6 +139,29 @@ const Format = ({ code, messageId, wrapper, onEvent, ...otherParams }: FormatPro
|
|
|
137
139
|
})
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
const openSkOverlay = async (appStoreId: string, position: string, dismissible: boolean) => {
|
|
143
|
+
try {
|
|
144
|
+
await NativeRNKontext.presentSKOverlay(appStoreId, position, dismissible)
|
|
145
|
+
sendMessage(webViewRef, 'update-skoverlay-iframe', code, {
|
|
146
|
+
open: true
|
|
147
|
+
})
|
|
148
|
+
} catch (e) {
|
|
149
|
+
console.error('error opening sk overlay', e)
|
|
150
|
+
reset()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const closeSkOverlay = async () => {
|
|
155
|
+
try {
|
|
156
|
+
await NativeRNKontext.dismissSKOverlay()
|
|
157
|
+
sendMessage(webViewRef, 'update-skoverlay-iframe', code, {
|
|
158
|
+
open: false
|
|
159
|
+
})
|
|
160
|
+
} catch (e) {
|
|
161
|
+
console.error('error dismissing sk overlay', e)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
140
165
|
debug('format-update-state')
|
|
141
166
|
|
|
142
167
|
const onMessage = (event: WebViewMessageEvent) => {
|
|
@@ -217,6 +242,14 @@ const Format = ({ code, messageId, wrapper, onEvent, ...otherParams }: FormatPro
|
|
|
217
242
|
context?.onAdEventInternal(message.data)
|
|
218
243
|
messageStatusRef.current = MessageStatus.MessageReceived
|
|
219
244
|
break
|
|
245
|
+
|
|
246
|
+
case 'open-skoverlay-iframe':
|
|
247
|
+
openSkOverlay(message.data.appStoreId, message.data.position, message.data.dismissible)
|
|
248
|
+
break
|
|
249
|
+
|
|
250
|
+
case 'close-skoverlay-iframe':
|
|
251
|
+
closeSkOverlay()
|
|
252
|
+
break
|
|
220
253
|
}
|
|
221
254
|
},
|
|
222
255
|
{
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Platform } from 'react-native'
|
|
2
|
+
import NativeRNKontext from '../NativeRNKontext';
|
|
3
|
+
|
|
4
|
+
export type SKOverlayPosition = 'bottom' | 'bottomRaised';
|
|
5
|
+
|
|
6
|
+
const isValidAppStoreId = (id: unknown): id is string => {
|
|
7
|
+
return typeof id === "string" && /^\d+$/.test(id);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const isValidPosition = (p: unknown): p is SKOverlayPosition => {
|
|
11
|
+
return p === "bottom" || p === "bottomRaised";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function presentSKOverlay(params: {
|
|
15
|
+
appStoreId: string;
|
|
16
|
+
position: SKOverlayPosition;
|
|
17
|
+
dismissible: boolean;
|
|
18
|
+
}) {
|
|
19
|
+
if (Platform.OS !== 'ios') {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
let { appStoreId, position, dismissible } = params
|
|
23
|
+
|
|
24
|
+
if (!isValidAppStoreId(appStoreId)) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
if (!isValidPosition(position)) {
|
|
28
|
+
position = 'bottom'
|
|
29
|
+
}
|
|
30
|
+
if (typeof dismissible !== 'boolean') {
|
|
31
|
+
dismissible = Boolean(dismissible)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return NativeRNKontext.presentSKOverlay(appStoreId, position, dismissible)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function dismissSKOverlay() {
|
|
38
|
+
if (Platform.OS !== 'ios') {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
return NativeRNKontext.dismissSKOverlay()
|
|
42
|
+
}
|