@kontextso/sdk-react-native 3.0.7-rc.3 → 4.0.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.
@@ -1,25 +0,0 @@
1
- package so.kontext.react
2
-
3
- import com.facebook.react.bridge.ReactApplicationContext
4
- import com.facebook.react.bridge.ReactContextBaseJavaModule
5
- import com.facebook.react.bridge.ReactMethod
6
- import com.facebook.react.bridge.Promise
7
- import android.content.Context
8
- import android.media.AudioManager
9
-
10
- class RNKontextModule(reactContext: ReactApplicationContext) :
11
- ReactContextBaseJavaModule(reactContext) {
12
-
13
- private val impl = RNKontextModuleImpl(reactContext)
14
-
15
- override fun getName(): String = NAME
16
-
17
- @ReactMethod
18
- fun isSoundOn(promise: Promise?) {
19
- impl.isSoundOn(promise)
20
- }
21
-
22
- companion object {
23
- const val NAME = "RNKontext"
24
- }
25
- }
@@ -1,16 +0,0 @@
1
- package so.kontext.react
2
-
3
- import com.facebook.react.ReactPackage
4
- import com.facebook.react.bridge.NativeModule
5
- import com.facebook.react.bridge.ReactApplicationContext
6
- import com.facebook.react.uimanager.ViewManager
7
-
8
- class RNKontextPackage : ReactPackage {
9
- override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
- return listOf(RNKontextModule(reactContext))
11
- }
12
-
13
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
- return emptyList()
15
- }
16
- }
@@ -1,26 +0,0 @@
1
- import AVFoundation
2
-
3
- @objc(KontextSDK)
4
- public class KontextSDK: NSObject {
5
- private static let MINIMAL_VOLUME_THRESHOLD: Float = 0.0
6
-
7
- @objc
8
- public static func isSoundOn() -> NSNumber? {
9
- let session = AVAudioSession.sharedInstance()
10
-
11
- do {
12
- try session.setCategory(
13
- .ambient,
14
- mode: .default,
15
- options: [.mixWithOthers]
16
- )
17
- try session.setActive(true)
18
-
19
- return NSNumber(
20
- value: session.outputVolume > MINIMAL_VOLUME_THRESHOLD
21
- )
22
- } catch {
23
- return nil
24
- }
25
- }
26
- }
package/ios/RNKontext.h DELETED
@@ -1,13 +0,0 @@
1
- #import "RNKontext-Swift.h"
2
-
3
- #ifdef RCT_NEW_ARCH_ENABLED
4
- #import <RNKontextSpec/RNKontextSpec.h>
5
-
6
- @interface RNKontext: NSObject <NativeRNKontextSpec>
7
- #else
8
- #import <React/RCTBridgeModule.h>
9
-
10
- @interface RNKontext: NSObject <RCTBridgeModule>
11
- #endif
12
-
13
- @end
package/ios/RNKontext.mm DELETED
@@ -1,36 +0,0 @@
1
- #import "RNKontext.h"
2
-
3
- @implementation RNKontext
4
-
5
- RCT_EXPORT_MODULE()
6
-
7
- #ifdef RCT_NEW_ARCH_ENABLED
8
- - (void)isSoundOn:(RCTPromiseResolveBlock)resolve
9
- reject:(RCTPromiseRejectBlock)reject {
10
- NSNumber *isSoundOn = [KontextSDK isSoundOn];
11
-
12
- if (isSoundOn == nil) {
13
- reject(@"soundon_error", @"Failed to read output volume", nil);
14
- } else {
15
- resolve(isSoundOn);
16
- }
17
- }
18
-
19
- - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
20
- (const facebook::react::ObjCTurboModule::InitParams &)params {
21
- return std::make_shared<facebook::react::NativeRNKontextSpecJSI>(params);
22
- }
23
- #else
24
- RCT_EXPORT_METHOD(isSoundOn : (RCTPromiseResolveBlock)
25
- resolve rejecter : (RCTPromiseRejectBlock)reject) {
26
- NSNumber *isSoundOn = [KontextSDK isSoundOn];
27
-
28
- if (isSoundOn == nil) {
29
- reject(@"soundon_error", @"Failed to read output volume", nil);
30
- } else {
31
- resolve(isSoundOn);
32
- }
33
- }
34
- #endif
35
-
36
- @end
@@ -1,9 +0,0 @@
1
- import { describe, expect, test } from 'vitest'
2
-
3
- describe('utils', () => {
4
- describe('test', () => {
5
- test('test', () => {
6
- expect(1).toBe(1)
7
- })
8
- })
9
- })
@@ -1,119 +0,0 @@
1
- import {
2
- AdsProviderInternal,
3
- type AdsProviderProps,
4
- type AppConfig,
5
- type DeviceConfig,
6
- log,
7
- type SDKConfig,
8
- } from '@kontextso/sdk-react'
9
- import { fetch as fetchNetworkInfo, NetInfoStateType } from '@react-native-community/netinfo'
10
- import { Appearance, Dimensions, PixelRatio, Platform } from 'react-native'
11
- import DeviceInfo, { type DeviceType } from 'react-native-device-info'
12
- import { version } from '../../package.json'
13
- import KontextSDK from '../NativeRNKontext'
14
-
15
- ErrorUtils.setGlobalHandler((error, isFatal) => {
16
- if (!isFatal) {
17
- log.warn(error)
18
- } else {
19
- log.error(error)
20
- }
21
- })
22
-
23
- const getDevice = async (): Promise<DeviceConfig> => {
24
- try {
25
- const powerState = await DeviceInfo.getPowerState()
26
- const deviceType = DeviceInfo.getDeviceType() as DeviceType
27
- const soundOn = await KontextSDK.isSoundOn()
28
- const screen = Dimensions.get('screen')
29
- const networkInfo = await fetchNetworkInfo()
30
-
31
- const mapDeviceTypeToHardwareType = (): DeviceConfig['hardware']['type'] => {
32
- switch (deviceType) {
33
- case 'Handset':
34
- return 'handset'
35
- case 'Tablet':
36
- return 'tablet'
37
- case 'Tv':
38
- return 'tv'
39
- case 'Desktop':
40
- return 'desktop'
41
- default:
42
- return 'other'
43
- }
44
- }
45
-
46
- return {
47
- hardware: {
48
- brand: DeviceInfo.getBrand(),
49
- model: DeviceInfo.getDeviceId(),
50
- type: mapDeviceTypeToHardwareType(),
51
- // bootTime: Not available without native module
52
- // sdCardAvailable: Not available without native module
53
- },
54
- audio: {
55
- muted: soundOn,
56
- // volume: Requires react-native-volume-manager
57
- // outputPluggedIn: Not available without native module
58
- // outputType: Not available without native module
59
- },
60
- network: {
61
- carrier: (networkInfo.type === NetInfoStateType.cellular && networkInfo.details.carrier) || undefined,
62
- userAgent: await DeviceInfo.getUserAgent(),
63
- type: [NetInfoStateType.wifi, NetInfoStateType.cellular, NetInfoStateType.ethernet].includes(networkInfo.type)
64
- ? (networkInfo.type as NetInfoStateType.wifi | NetInfoStateType.cellular | NetInfoStateType.ethernet)
65
- : NetInfoStateType.other,
66
- detail: (networkInfo.type === NetInfoStateType.cellular && networkInfo.details.cellularGeneration) || undefined,
67
- },
68
- os: {
69
- name: Platform.OS,
70
- version: DeviceInfo.getSystemVersion(),
71
- locale: Intl.DateTimeFormat().resolvedOptions().locale,
72
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
73
- },
74
- screen: {
75
- darkMode: Appearance.getColorScheme() === 'dark',
76
- dpr: PixelRatio.get(),
77
- height: screen.height,
78
- width: screen.width,
79
- orientation: screen.width > screen.height ? 'landscape' : 'portrait',
80
- },
81
- power: {
82
- batteryLevel: powerState.batteryLevel,
83
- batteryState: powerState.batteryState,
84
- lowPowerMode: powerState.lowPowerMode,
85
- },
86
- }
87
- } catch (error) {
88
- console.error(error)
89
- // @ts-expect-error
90
- return {}
91
- }
92
- }
93
-
94
- const getApp = async (): Promise<AppConfig> => {
95
- try {
96
- return {
97
- bundleId: DeviceInfo.getBundleId(),
98
- firstInstallTime: await DeviceInfo.getFirstInstallTime(),
99
- lastUpdateTime: await DeviceInfo.getLastUpdateTime(),
100
- version: DeviceInfo.getVersion(),
101
- // Not supported in react-native-device-info v10
102
- // startTime: await DeviceInfo.getStartupTime(),
103
- }
104
- } catch (error) {
105
- console.error(error)
106
- // @ts-expect-error
107
- return {}
108
- }
109
- }
110
-
111
- const getSdk = async (): Promise<SDKConfig> => ({
112
- name: 'sdk-react-native',
113
- platform: Platform.OS === 'ios' ? 'ios' : 'android',
114
- version,
115
- })
116
-
117
- export const AdsProvider = (props: AdsProviderProps) => {
118
- return <AdsProviderInternal {...props} getDevice={getDevice} getSdk={getSdk} getApp={getApp} />
119
- }
@@ -1,256 +0,0 @@
1
- import {
2
- handleIframeMessage,
3
- type IframeMessage,
4
- type IframeMessageEvent,
5
- type IframeMessageType,
6
- makeIframeMessage,
7
- } from '@kontextso/sdk-common'
8
- import {
9
- AdsContext,
10
- type FormatProps,
11
- useBid,
12
- } from '@kontextso/sdk-react'
13
- import { useContext, useRef } from 'react'
14
- import { View } from 'react-native'
15
- import { WebView, type WebViewMessageEvent } from 'react-native-webview'
16
-
17
- const sendMessage = (
18
- webViewRef: React.RefObject<WebView>,
19
- type: Extract<IframeMessageType, 'update-iframe' | 'update-dimensions-iframe'>,
20
- code: string,
21
- data: any
22
- ) => {
23
- const message = makeIframeMessage(type, {
24
- data,
25
- code,
26
- })
27
-
28
- webViewRef.current?.injectJavaScript(`
29
- window.dispatchEvent(new MessageEvent('message', {
30
- data: ${JSON.stringify(message)}
31
- }));
32
- `)
33
- }
34
-
35
- const getUrl = (code: string, messageId: string, bidId?: string) => {
36
- const context = useContext(AdsContext)
37
- if (!context || !bidId) {
38
- return null
39
- }
40
-
41
- const adServerUrl = context?.adServerUrl
42
-
43
- const params = new URLSearchParams({
44
- code,
45
- messageId,
46
- sdk: 'sdk-react-native',
47
- })
48
-
49
- return `${adServerUrl}/api/frame/${bidId}?${params}`
50
- }
51
-
52
- const Format = ({ code, messageId, wrapper, onEvent, ...otherParams }: FormatProps) => {
53
- const context = useContext(AdsContext)
54
-
55
- const bid = useBid({ code, messageId })
56
- const iframeUrl = getUrl(code, messageId, bid?.bidId)
57
-
58
- const webViewRef = useRef<WebView>(null)
59
-
60
- const debug = (name: string, data: any = {}) => {
61
- context?.onDebugEventInternal?.(name, {
62
- code,
63
- messageId,
64
- otherParams,
65
- bid,
66
- iframeUrl,
67
- ...data,
68
- })
69
- }
70
-
71
- debug('Format:updateState', {
72
- params: {
73
- messageId,
74
- code,
75
- otherParams
76
- }
77
- })
78
-
79
- const onMessage = (event: WebViewMessageEvent) => {
80
- try {
81
- const data = JSON.parse(event.nativeEvent.data) as IframeMessage
82
-
83
- debug('Format:iframeMessage', {
84
- message: data,
85
- params: { data, messageId, code, otherParams }
86
- })
87
-
88
- const messageHandler = handleIframeMessage(
89
- (message) => {
90
- switch (message.type) {
91
- case 'init-iframe':
92
- debug('Format:iframePostMessage', {
93
- params: {
94
- code,
95
- messages: context?.messages,
96
- sdk: 'sdk-react-native',
97
- otherParams,
98
- messageId,
99
- }
100
- })
101
- sendMessage(webViewRef, 'update-iframe', code, {
102
- messages: context?.messages,
103
- sdk: 'sdk-react-native',
104
- otherParams,
105
- messageId,
106
- })
107
- break
108
-
109
- case 'event-iframe':
110
- onEvent?.(message.data)
111
- context?.onAdEventInternal(message.data)
112
- break
113
- }
114
- },
115
- {
116
- code,
117
- }
118
- )
119
- messageHandler({ data } as IframeMessageEvent)
120
- } catch (e) {
121
- debug('Format:iframeMessageError', {
122
- params: { error: e, messageId, code, otherParams },
123
- error: e,
124
- })
125
- console.error('error parsing message from webview', e)
126
- }
127
- }
128
-
129
- if (!context || !bid || !iframeUrl) {
130
- debug('Format:noContextOrBidOrIframeUrl', {
131
- params: {
132
- context,
133
- bid,
134
- iframeUrl,
135
- messageId,
136
- code,
137
- otherParams,
138
- }
139
- })
140
- return null
141
- }
142
-
143
- return (
144
- <View
145
- style={{
146
- height: 300,
147
- width: '100%',
148
- backgroundColor: 'transparent',
149
- borderWidth: 0,
150
- }}
151
- >
152
-
153
- <WebView
154
- ref={webViewRef}
155
- source={{
156
- uri: iframeUrl,
157
- }}
158
- onMessage={onMessage}
159
- style={{
160
- height: 300,
161
- width: '100%',
162
- backgroundColor: 'transparent',
163
- borderWidth: 0,
164
- }}
165
- allowsInlineMediaPlayback={true}
166
- mediaPlaybackRequiresUserAction={false}
167
- javaScriptEnabled={true}
168
- domStorageEnabled={true}
169
- allowsFullscreenVideo={false}
170
- injectedJavaScript={`
171
- window.addEventListener("message", function(event) {
172
- if (window.ReactNativeWebView && event.data) {
173
- // ReactNativeWebView.postMessage only supports string data
174
- window.ReactNativeWebView.postMessage(JSON.stringify(event.data));
175
- }
176
- }, false);
177
- `}
178
- onLoadStart={() => {
179
- debug('Format:iframeLoadStart', {
180
- params: {
181
- messageId,
182
- code,
183
- otherParams,
184
- }
185
- })
186
- }}
187
- onError={() => {
188
- debug('Format:iframeError', {
189
- params: {
190
- messageId,
191
- code,
192
- otherParams,
193
- }
194
- })
195
- }}
196
- onLoad={() => {
197
- debug('Format:iframeLoad', {
198
- params: {
199
- messageId,
200
- code,
201
- otherParams,
202
- }
203
- })
204
- }}
205
- onLoadProgress={() => {
206
- debug('Format:iframeLoadProgress', {
207
- params: {
208
- messageId,
209
- code,
210
- otherParams,
211
- }
212
- })
213
- }}
214
- onHttpError={() => {
215
- debug('Format:iframeHttpError', {
216
- params: {
217
- messageId,
218
- code,
219
- otherParams,
220
- }
221
- })
222
- }}
223
- onRenderProcessGone={() => {
224
- debug('Format:iframeRenderProcessGone', {
225
- params: {
226
- messageId,
227
- code,
228
- otherParams,
229
- }
230
- })
231
- }}
232
- onNavigationStateChange={() => {
233
- debug('Format:iframeNavigationStateChange', {
234
- params: {
235
- messageId,
236
- code,
237
- otherParams,
238
- }
239
- })
240
- }}
241
- onContentProcessDidTerminate={() => {
242
- debug('Format:iframeContentProcessDidTerminate', {
243
- params: {
244
- messageId,
245
- code,
246
- otherParams,
247
- }
248
- })
249
- }}
250
-
251
- />
252
- </View>
253
- )
254
- }
255
-
256
- export default Format
@@ -1,8 +0,0 @@
1
- import Format from './Format'
2
- import type { FormatProps } from '@kontextso/sdk-react'
3
-
4
- const InlineAd = ({ code, messageId, wrapper, ...props }: FormatProps) => {
5
- return <Format code={code} messageId={messageId} wrapper={wrapper} {...props} />
6
- }
7
-
8
- export default InlineAd
@@ -1,43 +0,0 @@
1
- import { forwardRef } from 'react'
2
- import type { StyleProp, ViewStyle } from 'react-native'
3
- import { WebView, type WebViewMessageEvent } from 'react-native-webview'
4
-
5
- interface FrameWebViewProps {
6
- iframeUrl: string
7
- onMessage: (event: WebViewMessageEvent) => void
8
- style: StyleProp<ViewStyle>
9
- onError: () => void
10
- onLoad: () => void
11
- }
12
-
13
- const FrameWebView = forwardRef<WebView, FrameWebViewProps>(
14
- ({ iframeUrl, onMessage, style, onError, onLoad }, forwardedRef) => {
15
- return (
16
- <WebView
17
- ref={forwardedRef}
18
- source={{
19
- uri: iframeUrl,
20
- }}
21
- onMessage={onMessage}
22
- style={style}
23
- allowsInlineMediaPlayback={true}
24
- mediaPlaybackRequiresUserAction={false}
25
- javaScriptEnabled={true}
26
- domStorageEnabled={true}
27
- allowsFullscreenVideo={false}
28
- injectedJavaScript={`
29
- window.addEventListener("message", function(event) {
30
- if (window.ReactNativeWebView && event.data) {
31
- // ReactNativeWebView.postMessage only supports string data
32
- window.ReactNativeWebView.postMessage(JSON.stringify(event.data));
33
- }
34
- }, false);
35
- `}
36
- onError={onError}
37
- onLoad={onLoad}
38
- />
39
- )
40
- }
41
- )
42
-
43
- export default FrameWebView