@kontextso/sdk-react-native 3.0.7-rc.3 → 4.0.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.d.mts +167 -4
- package/dist/index.d.ts +167 -4
- package/dist/index.js +471 -298
- package/dist/index.mjs +471 -294
- package/package.json +7 -9
- package/src/AbstractStream.ts +141 -0
- package/src/Configuration.ts +129 -0
- package/src/InlineAd.tsx +167 -0
- package/src/KontextAds.ts +40 -0
- package/src/Logger.ts +95 -0
- package/src/NativeStream.ts +20 -0
- package/src/Preload.ts +150 -0
- package/src/Session.ts +104 -0
- package/src/index.ts +2 -4
- package/src/utils/request.ts +73 -0
- package/src/utils/sdk.ts +16 -0
- package/android/build.gradle +0 -88
- package/android/gradle.properties +0 -5
- package/android/src/main/AndroidManifest.xml +0 -2
- package/android/src/main/java/so/kontext/react/RNKontextModuleImpl.kt +0 -21
- package/android/src/newarch/java/so/kontext/react/RNKontextModule.kt +0 -22
- package/android/src/newarch/java/so/kontext/react/RNKontextPackage.kt +0 -32
- package/android/src/oldarch/java/so/kontext/react/RNKontextModule.kt +0 -25
- package/android/src/oldarch/java/so/kontext/react/RNKontextPacakge.kt +0 -16
- package/ios/KontextSDK.swift +0 -26
- package/ios/RNKontext.h +0 -13
- package/ios/RNKontext.mm +0 -36
- package/src/__tests__/util.test.ts +0 -9
- package/src/context/AdsProvider.tsx +0 -119
- package/src/formats/Format.tsx +0 -256
- package/src/formats/InlineAd.tsx +0 -8
- package/src/frame-webview.tsx +0 -43
package/src/Preload.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { Message } from '@kontextso/sdk-common'
|
|
2
|
+
import type{ Session } from './Session'
|
|
3
|
+
import { fetchWithRetry } from './utils/request'
|
|
4
|
+
|
|
5
|
+
type Bid = {
|
|
6
|
+
bidId: string
|
|
7
|
+
code: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface PreloadResponse {
|
|
11
|
+
bids?: Bid[]
|
|
12
|
+
sessionId?: string
|
|
13
|
+
skipCode?: string
|
|
14
|
+
skip?: boolean
|
|
15
|
+
|
|
16
|
+
// Error state
|
|
17
|
+
error?: string
|
|
18
|
+
errCode?: string
|
|
19
|
+
permanent?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class Preload {
|
|
23
|
+
public readonly session: Session
|
|
24
|
+
|
|
25
|
+
private messages: Message[] = []
|
|
26
|
+
private abortController: AbortController
|
|
27
|
+
private running = false
|
|
28
|
+
private bid: Bid | null = null
|
|
29
|
+
|
|
30
|
+
constructor(session: Session, messages: Message[]) {
|
|
31
|
+
this.session = session
|
|
32
|
+
this.abortController = new AbortController()
|
|
33
|
+
this.setMessages(messages)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private setMessages (messages: Message[]) {
|
|
37
|
+
this.messages = messages
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
hasBid () {
|
|
41
|
+
return this.bid !== null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getBid () {
|
|
45
|
+
return this.bid
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private setBid (bid: Bid) {
|
|
49
|
+
this.bid = bid
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
cancel () {
|
|
53
|
+
this.abortController.abort()
|
|
54
|
+
this.running = false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
isRunning () {
|
|
58
|
+
return this.running
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
private async getDevice () {
|
|
63
|
+
try {
|
|
64
|
+
return await getDevice()
|
|
65
|
+
} catch (error) {
|
|
66
|
+
this.session.logger.error('Error getting device:', error)
|
|
67
|
+
return {} as DeviceConfig
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
private async getPreloadBody () {
|
|
73
|
+
const config = this.session.config
|
|
74
|
+
const preloadBody: Record<string, any> = {
|
|
75
|
+
messages: this.messages,
|
|
76
|
+
sessionId: this.session.getSessionId(),
|
|
77
|
+
publisherToken: config.publisherToken,
|
|
78
|
+
userId: config.userId,
|
|
79
|
+
userEmail: config.userEmail,
|
|
80
|
+
conversationId: config.conversationId,
|
|
81
|
+
character: config.character,
|
|
82
|
+
enabledPlacementCodes: [config.placementCode],
|
|
83
|
+
variantId: config.variantId,
|
|
84
|
+
regulatory: config.regulatory,
|
|
85
|
+
sdk: this.session.sdk,
|
|
86
|
+
}
|
|
87
|
+
return preloadBody
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private canRequestAd () {
|
|
91
|
+
if (this.session.isSessionDisabled()) {
|
|
92
|
+
return {
|
|
93
|
+
status: 'error',
|
|
94
|
+
message: 'Session is disabled',
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (!this.messages.length) {
|
|
98
|
+
return {
|
|
99
|
+
status: 'error',
|
|
100
|
+
message: 'No messages',
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async requestAd () {
|
|
107
|
+
this.running = true
|
|
108
|
+
const cannotRequestAdReason = this.canRequestAd()
|
|
109
|
+
if (cannotRequestAdReason) {
|
|
110
|
+
return cannotRequestAdReason
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const config = this.session.config
|
|
114
|
+
const preloadBody = await this.getPreloadBody()
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const response = await fetchWithRetry(`${config.adServerUrl}/preload`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
body: JSON.stringify(preloadBody),
|
|
120
|
+
headers: {
|
|
121
|
+
'Kontextso-Is-Disabled': '0',
|
|
122
|
+
'Kontextso-Publisher-Token': config.publisherToken,
|
|
123
|
+
},
|
|
124
|
+
timeout: 16000,
|
|
125
|
+
abortController: this.abortController,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const jsonResponse = await response.json() as PreloadResponse
|
|
129
|
+
|
|
130
|
+
const bid = jsonResponse.bids?.[0] ?? null
|
|
131
|
+
if (jsonResponse.sessionId) {
|
|
132
|
+
this.session.setSessionId(jsonResponse.sessionId)
|
|
133
|
+
}
|
|
134
|
+
if (bid) {
|
|
135
|
+
this.setBid(bid)
|
|
136
|
+
}
|
|
137
|
+
this.session.updateBids()
|
|
138
|
+
return {
|
|
139
|
+
status: 'success',
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
this.session.logger.error('Error requesting ad:', error)
|
|
143
|
+
return {
|
|
144
|
+
status: 'error',
|
|
145
|
+
}
|
|
146
|
+
} finally {
|
|
147
|
+
this.running = false
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
package/src/Session.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Message } from '@kontextso/sdk-common'
|
|
2
|
+
import type { Configuration } from './Configuration'
|
|
3
|
+
import { Logger } from './Logger'
|
|
4
|
+
import { Preload } from './Preload'
|
|
5
|
+
import type { SDKConfig } from './utils/sdk'
|
|
6
|
+
|
|
7
|
+
export class Session {
|
|
8
|
+
public readonly config: Configuration
|
|
9
|
+
public readonly logger: Logger
|
|
10
|
+
public readonly sdk: SDKConfig
|
|
11
|
+
|
|
12
|
+
private sessionId: string | null = null
|
|
13
|
+
private sessionDisabled = false
|
|
14
|
+
private messages: Message[] = []
|
|
15
|
+
private bids: {
|
|
16
|
+
bidId: string
|
|
17
|
+
messageId: string
|
|
18
|
+
}[] = []
|
|
19
|
+
private onUpdateBidsCallback: (() => void) | null = null
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// only one preload instance is allowed at a time
|
|
23
|
+
private preloadInstance: Preload | null = null
|
|
24
|
+
|
|
25
|
+
constructor(config: Configuration, { sdk }: { sdk: SDKConfig }) {
|
|
26
|
+
this.config = config
|
|
27
|
+
this.logger = new Logger()
|
|
28
|
+
this.sdk = sdk
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setOnUpdateBids (callback: () => void) {
|
|
32
|
+
this.onUpdateBidsCallback = callback
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
isSessionDisabled () {
|
|
36
|
+
return this.sessionDisabled
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setSessionDisabled (disabled: boolean) {
|
|
40
|
+
this.sessionDisabled = disabled
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getSessionId () {
|
|
44
|
+
return this.sessionId
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setSessionId (sessionId: string) {
|
|
48
|
+
this.logger.info('Session ID set:', sessionId)
|
|
49
|
+
this.sessionId = sessionId
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
addMessage (message: Message) {
|
|
53
|
+
this.messages.push(message)
|
|
54
|
+
this.updateBids()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getMessages () {
|
|
58
|
+
return this.messages
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public updateBids() {
|
|
62
|
+
if (!this.preloadInstance?.hasBid()) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
const bid = this.preloadInstance.getBid()
|
|
66
|
+
if (!bid) {
|
|
67
|
+
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
const lastMessage = this.messages[this.messages.length - 1]
|
|
71
|
+
if (!lastMessage || lastMessage.role !== 'assistant') {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
const assignedBid = this.bids.find((b) => {
|
|
75
|
+
if (b.bidId === bid?.bidId) {
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
if (b.messageId === lastMessage.id) {
|
|
79
|
+
return true
|
|
80
|
+
}
|
|
81
|
+
return false
|
|
82
|
+
})
|
|
83
|
+
if (assignedBid) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
this.bids.push({
|
|
87
|
+
bidId: bid.bidId,
|
|
88
|
+
messageId: lastMessage.id,
|
|
89
|
+
})
|
|
90
|
+
this.onUpdateBidsCallback?.()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
preload () {
|
|
94
|
+
if (this.preloadInstance?.isRunning()) {
|
|
95
|
+
this.preloadInstance.cancel()
|
|
96
|
+
}
|
|
97
|
+
this.preloadInstance = new Preload(this, [...this.messages])
|
|
98
|
+
return this.preloadInstance
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getLastBid () {
|
|
102
|
+
return this.bids[this.bids.length - 1]
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request function with retry and exponential backoff
|
|
3
|
+
*/
|
|
4
|
+
export async function fetchWithRetry(
|
|
5
|
+
url: string,
|
|
6
|
+
options: RequestInit & {
|
|
7
|
+
retry?: {
|
|
8
|
+
maxRetries?: number
|
|
9
|
+
baseDelay?: number
|
|
10
|
+
backoffFactor?: number
|
|
11
|
+
}
|
|
12
|
+
timeout?: number
|
|
13
|
+
abortController?: AbortController
|
|
14
|
+
} = {}
|
|
15
|
+
): Promise<Response> {
|
|
16
|
+
const { retry = {}, timeout, abortController: abortControllerOption, ...fetchOptions } = options
|
|
17
|
+
|
|
18
|
+
const maxRetries = retry.maxRetries ?? 3
|
|
19
|
+
const baseDelay = retry.baseDelay ?? 1000
|
|
20
|
+
const backoffFactor = retry.backoffFactor ?? 2
|
|
21
|
+
|
|
22
|
+
const abortController = abortControllerOption || new AbortController()
|
|
23
|
+
fetchOptions.signal = abortController.signal
|
|
24
|
+
|
|
25
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null
|
|
26
|
+
if (timeout) {
|
|
27
|
+
timeoutId = setTimeout(() => abortController.abort('timeout'), timeout)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let lastError: Error | null = null
|
|
31
|
+
|
|
32
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(url, fetchOptions)
|
|
35
|
+
|
|
36
|
+
// Throw only re-tryable errors, otherwise return the response to let the caller handle it
|
|
37
|
+
if (response.status >= 500 || response.status === 429) {
|
|
38
|
+
const error = new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
39
|
+
;(error as any).status = response.status
|
|
40
|
+
throw error
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return response
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (abortController.signal.aborted) {
|
|
46
|
+
throw new Error(`Fetch aborted: ${abortController.signal.reason}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lastError = error instanceof Error ? error : new Error('Unknown error')
|
|
50
|
+
|
|
51
|
+
// Retry on network errors and 5xx status codes
|
|
52
|
+
const shouldRetry =
|
|
53
|
+
error instanceof TypeError || // Network error
|
|
54
|
+
(error as any).status >= 500 || // Server error
|
|
55
|
+
(error as any).status === 429 // Rate limited
|
|
56
|
+
|
|
57
|
+
// Don't retry on last attempt or if error shouldn't be retried
|
|
58
|
+
if (attempt === maxRetries || !shouldRetry) {
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Wait before retry (exponential backoff)
|
|
63
|
+
const delay = baseDelay * backoffFactor ** attempt
|
|
64
|
+
await new Promise((resolve) => setTimeout(resolve, delay))
|
|
65
|
+
} finally {
|
|
66
|
+
if (timeoutId) {
|
|
67
|
+
clearTimeout(timeoutId)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
throw lastError
|
|
73
|
+
}
|
package/src/utils/sdk.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
import type { SDK } from '@kontextso/sdk-common'
|
|
3
|
+
import { Platform } from 'react-native'
|
|
4
|
+
import { version } from '../../package.json'
|
|
5
|
+
|
|
6
|
+
export interface SDKConfig {
|
|
7
|
+
name: Exclude<SDK, 'sdk'>
|
|
8
|
+
platform: 'ios' | 'android' | 'web'
|
|
9
|
+
version: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const getSdk = async (): Promise<SDKConfig> => ({
|
|
13
|
+
name: 'sdk-react-native',
|
|
14
|
+
platform: Platform.OS === 'ios' ? 'ios' : 'android',
|
|
15
|
+
version,
|
|
16
|
+
})
|
package/android/build.gradle
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
buildscript {
|
|
2
|
-
ext.getExtOrDefault = {name ->
|
|
3
|
-
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['RNKontext_' + name]
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
repositories {
|
|
7
|
-
google()
|
|
8
|
-
mavenCentral()
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
dependencies {
|
|
12
|
-
classpath "com.android.tools.build:gradle:8.7.2"
|
|
13
|
-
// noinspection DifferentKotlinGradleVersion
|
|
14
|
-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
apply plugin: "com.android.library"
|
|
20
|
-
apply plugin: "kotlin-android"
|
|
21
|
-
|
|
22
|
-
apply plugin: "com.facebook.react"
|
|
23
|
-
|
|
24
|
-
def isNewArchitectureEnabled() {
|
|
25
|
-
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
def getExtOrIntegerDefault(name) {
|
|
29
|
-
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["RNKontext_" + name]).toInteger()
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
android {
|
|
33
|
-
namespace "so.kontext.react"
|
|
34
|
-
|
|
35
|
-
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
36
|
-
|
|
37
|
-
defaultConfig {
|
|
38
|
-
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
39
|
-
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
40
|
-
// Expose the new-arch flag to runtime and build-time Kotlin/Java code
|
|
41
|
-
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
buildFeatures {
|
|
45
|
-
buildConfig true
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
buildTypes {
|
|
49
|
-
release {
|
|
50
|
-
minifyEnabled false
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
lintOptions {
|
|
55
|
-
disable "GradleCompatible"
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
compileOptions {
|
|
59
|
-
sourceCompatibility JavaVersion.VERSION_1_8
|
|
60
|
-
targetCompatibility JavaVersion.VERSION_1_8
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
sourceSets {
|
|
64
|
-
main {
|
|
65
|
-
java.srcDirs += [
|
|
66
|
-
"generated/java",
|
|
67
|
-
"generated/jni"
|
|
68
|
-
]
|
|
69
|
-
if (isNewArchitectureEnabled()) {
|
|
70
|
-
java.srcDirs += ['src/newarch/java']
|
|
71
|
-
} else {
|
|
72
|
-
java.srcDirs += ['src/oldarch/java']
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
repositories {
|
|
79
|
-
mavenCentral()
|
|
80
|
-
google()
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
84
|
-
|
|
85
|
-
dependencies {
|
|
86
|
-
implementation "com.facebook.react:react-android"
|
|
87
|
-
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
88
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
package so.kontext.react
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.bridge.Promise
|
|
4
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
-
import android.content.Context
|
|
6
|
-
import android.media.AudioManager
|
|
7
|
-
|
|
8
|
-
class RNKontextModuleImpl(private val reactContext: ReactApplicationContext) {
|
|
9
|
-
fun isSoundOn(promise: Promise?) {
|
|
10
|
-
val audioManager = reactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
11
|
-
val current = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
|
12
|
-
val max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
|
13
|
-
val volume = if (max > 0) current.toFloat() / max.toFloat() else 0f
|
|
14
|
-
|
|
15
|
-
promise?.resolve(volume > MINIMAL_VOLUME_THRESHOLD)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
companion object {
|
|
19
|
-
private const val MINIMAL_VOLUME_THRESHOLD = 0.0f
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
package so.kontext.react
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
|
-
import com.facebook.react.module.annotations.ReactModule
|
|
5
|
-
import com.facebook.react.bridge.Promise
|
|
6
|
-
|
|
7
|
-
@ReactModule(name = RNKontextModule.NAME)
|
|
8
|
-
class RNKontextModule(reactContext: ReactApplicationContext) :
|
|
9
|
-
NativeRNKontextSpec(reactContext) {
|
|
10
|
-
|
|
11
|
-
private val impl = RNKontextModuleImpl(reactContext)
|
|
12
|
-
|
|
13
|
-
override fun getName(): String = NAME
|
|
14
|
-
|
|
15
|
-
override fun isSoundOn(promise: Promise?) {
|
|
16
|
-
impl.isSoundOn(promise)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
companion object {
|
|
20
|
-
const val NAME = "RNKontext"
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
package so.kontext.react
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.BaseReactPackage
|
|
4
|
-
import com.facebook.react.bridge.NativeModule
|
|
5
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
-
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
-
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
-
|
|
9
|
-
class RNKontextPackage : BaseReactPackage() {
|
|
10
|
-
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
11
|
-
return if (name == RNKontextModule.NAME) {
|
|
12
|
-
RNKontextModule(reactContext)
|
|
13
|
-
} else {
|
|
14
|
-
null
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
19
|
-
return ReactModuleInfoProvider {
|
|
20
|
-
mapOf(
|
|
21
|
-
RNKontextModule.NAME to ReactModuleInfo(
|
|
22
|
-
RNKontextModule.NAME,
|
|
23
|
-
RNKontextModule.NAME,
|
|
24
|
-
false, // canOverrideExistingModule
|
|
25
|
-
false, // needsEagerInit
|
|
26
|
-
false, // isCxxModule
|
|
27
|
-
true // isTurboModule
|
|
28
|
-
)
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -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
|
-
}
|
package/ios/KontextSDK.swift
DELETED
|
@@ -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
|