@octopus-community/react-native 1.0.0 → 1.0.3
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 +308 -3
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactNativeSdkModule.kt +82 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt +149 -1
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeConfig.kt +22 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeManager.kt +11 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt +234 -38
- package/ios/OctopusColorUtility.swift +44 -0
- package/ios/OctopusReactNativeSdk.mm +8 -0
- package/ios/OctopusReactNativeSdk.swift +52 -28
- package/ios/OctopusSDKInitializer.swift +136 -6
- package/ios/OctopusUIManager.swift +138 -7
- package/lib/module/initialize.js +54 -1
- package/lib/module/initialize.js.map +1 -1
- package/lib/module/internals/colorSchemeManager.js +105 -0
- package/lib/module/internals/colorSchemeManager.js.map +1 -0
- package/lib/module/internals/fontParser.js +48 -0
- package/lib/module/internals/fontParser.js.map +1 -0
- package/lib/typescript/src/initialize.d.ts +92 -0
- package/lib/typescript/src/initialize.d.ts.map +1 -1
- package/lib/typescript/src/internals/colorSchemeManager.d.ts +39 -0
- package/lib/typescript/src/internals/colorSchemeManager.d.ts.map +1 -0
- package/lib/typescript/src/internals/fontParser.d.ts +18 -0
- package/lib/typescript/src/internals/fontParser.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/initialize.ts +131 -1
- package/src/internals/colorSchemeManager.ts +113 -0
- package/src/internals/fontParser.ts +64 -0
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt
CHANGED
|
@@ -4,68 +4,264 @@ import android.content.BroadcastReceiver
|
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.content.Intent
|
|
6
6
|
import android.content.IntentFilter
|
|
7
|
+
import android.content.res.Configuration
|
|
8
|
+
import android.graphics.BitmapFactory
|
|
7
9
|
import android.os.Build
|
|
8
10
|
import android.os.Bundle
|
|
11
|
+
import android.util.Log
|
|
9
12
|
import androidx.activity.ComponentActivity
|
|
10
13
|
import androidx.activity.compose.setContent
|
|
11
14
|
import androidx.compose.foundation.layout.Box
|
|
12
15
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
13
16
|
import androidx.compose.runtime.Composable
|
|
17
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
18
|
+
import androidx.compose.runtime.getValue
|
|
19
|
+
import androidx.compose.runtime.mutableStateOf
|
|
20
|
+
import androidx.compose.runtime.remember
|
|
21
|
+
import androidx.compose.runtime.setValue
|
|
14
22
|
import androidx.compose.ui.Modifier
|
|
23
|
+
import androidx.compose.ui.graphics.Color
|
|
24
|
+
import androidx.compose.ui.graphics.asImageBitmap
|
|
25
|
+
import androidx.compose.ui.graphics.painter.BitmapPainter
|
|
26
|
+
import androidx.compose.ui.graphics.painter.Painter
|
|
27
|
+
import androidx.compose.ui.platform.LocalContext
|
|
15
28
|
import androidx.navigation.compose.NavHost
|
|
29
|
+
import androidx.navigation.compose.composable
|
|
16
30
|
import androidx.navigation.compose.rememberNavController
|
|
17
|
-
import com.octopuscommunity.sdk.ui.
|
|
31
|
+
import com.octopuscommunity.sdk.ui.OctopusDrawablesDefaults
|
|
32
|
+
import com.octopuscommunity.sdk.ui.OctopusTheme
|
|
33
|
+
import com.octopuscommunity.sdk.ui.OctopusTypographyDefaults
|
|
34
|
+
import com.octopuscommunity.sdk.ui.OctopusTypography
|
|
35
|
+
import com.octopuscommunity.sdk.ui.components.OctopusNavigationHandler
|
|
36
|
+
import androidx.compose.ui.text.TextStyle
|
|
37
|
+
import androidx.compose.ui.text.font.FontFamily
|
|
38
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
39
|
+
import androidx.compose.ui.unit.sp
|
|
40
|
+
import com.octopuscommunity.sdk.ui.home.OctopusHomeScreen
|
|
18
41
|
import com.octopuscommunity.sdk.ui.octopusComposables
|
|
42
|
+
import com.octopuscommunity.sdk.ui.octopusDarkColorScheme
|
|
43
|
+
import com.octopuscommunity.sdk.ui.octopusLightColorScheme
|
|
44
|
+
import kotlinx.coroutines.Dispatchers
|
|
45
|
+
import kotlinx.coroutines.withContext
|
|
46
|
+
import java.net.URL
|
|
19
47
|
|
|
20
48
|
class OctopusUIActivity : ComponentActivity() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
49
|
+
private val closeUIReceiver = object : BroadcastReceiver() {
|
|
50
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
51
|
+
finish()
|
|
52
|
+
}
|
|
24
53
|
}
|
|
25
|
-
}
|
|
26
54
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
55
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
56
|
+
super.onCreate(savedInstanceState)
|
|
57
|
+
registerCloseUIReceiver()
|
|
58
|
+
setContent {
|
|
59
|
+
OctopusUI(onBack = { finish() })
|
|
60
|
+
}
|
|
32
61
|
}
|
|
33
|
-
}
|
|
34
62
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
63
|
+
override fun onDestroy() {
|
|
64
|
+
super.onDestroy()
|
|
65
|
+
unregisterReceiver(closeUIReceiver)
|
|
66
|
+
}
|
|
39
67
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
68
|
+
private fun registerCloseUIReceiver() {
|
|
69
|
+
val intentFilter = IntentFilter(OctopusUIController.CLOSE_UI_ACTION)
|
|
70
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
71
|
+
registerReceiver(closeUIReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
72
|
+
} else {
|
|
73
|
+
@Suppress("UnspecifiedRegisterReceiverFlag")
|
|
74
|
+
registerReceiver(closeUIReceiver, intentFilter)
|
|
75
|
+
}
|
|
47
76
|
}
|
|
48
|
-
}
|
|
49
77
|
}
|
|
50
78
|
|
|
51
79
|
@Composable
|
|
52
|
-
private fun OctopusUI() {
|
|
53
|
-
|
|
54
|
-
|
|
80
|
+
private fun OctopusUI(onBack: () -> Unit) {
|
|
81
|
+
val context = LocalContext.current
|
|
82
|
+
|
|
83
|
+
// Get theme config once when UI is created - no need for polling
|
|
84
|
+
val themeConfig = OctopusThemeManager.getThemeConfig()
|
|
85
|
+
|
|
86
|
+
// Function to detect if system is in dark mode
|
|
87
|
+
fun isSystemInDarkTheme(): Boolean {
|
|
88
|
+
return (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Determine base color scheme based on theme config or system theme
|
|
92
|
+
val baseColorScheme = if (themeConfig?.colorScheme == "dark") {
|
|
93
|
+
octopusDarkColorScheme()
|
|
94
|
+
} else if (themeConfig?.colorScheme == "light") {
|
|
95
|
+
octopusLightColorScheme()
|
|
96
|
+
} else if (isSystemInDarkTheme()) {
|
|
97
|
+
octopusDarkColorScheme()
|
|
98
|
+
} else {
|
|
99
|
+
octopusLightColorScheme()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Apply custom colors if theme config is provided
|
|
103
|
+
val finalColorScheme = if (themeConfig != null) {
|
|
104
|
+
baseColorScheme.copy(
|
|
105
|
+
primary = themeConfig.primaryColor?.let {
|
|
106
|
+
Color(android.graphics.Color.parseColor(it))
|
|
107
|
+
} ?: baseColorScheme.primary,
|
|
108
|
+
primaryLow = themeConfig.primaryLowContrastColor?.let {
|
|
109
|
+
Color(android.graphics.Color.parseColor(it))
|
|
110
|
+
} ?: baseColorScheme.primaryLow,
|
|
111
|
+
primaryHigh = themeConfig.primaryHighContrastColor?.let {
|
|
112
|
+
Color(android.graphics.Color.parseColor(it))
|
|
113
|
+
} ?: baseColorScheme.primaryHigh,
|
|
114
|
+
onPrimary = themeConfig.onPrimaryColor?.let {
|
|
115
|
+
Color(android.graphics.Color.parseColor(it))
|
|
116
|
+
} ?: baseColorScheme.onPrimary
|
|
117
|
+
)
|
|
118
|
+
} else {
|
|
119
|
+
baseColorScheme
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Handle logo loading using built-in Android capabilities
|
|
123
|
+
var logoPainter by remember { mutableStateOf<Painter?>(null) }
|
|
124
|
+
|
|
125
|
+
LaunchedEffect(themeConfig) {
|
|
126
|
+
themeConfig?.logoSource?.let { logoSource ->
|
|
127
|
+
val uri = logoSource.getString("uri")
|
|
128
|
+
if (uri != null) {
|
|
129
|
+
logoPainter = loadImageFromUri(uri)
|
|
130
|
+
} else {
|
|
131
|
+
logoPainter = null
|
|
132
|
+
}
|
|
133
|
+
} ?: run {
|
|
134
|
+
// No theme config or no logo source
|
|
135
|
+
logoPainter = null
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Create drawables based on theme config
|
|
140
|
+
val drawables = if (logoPainter != null) {
|
|
141
|
+
OctopusDrawablesDefaults.drawables(logo = logoPainter)
|
|
142
|
+
} else {
|
|
143
|
+
OctopusDrawablesDefaults.drawables()
|
|
144
|
+
}
|
|
55
145
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
146
|
+
// Create typography based on theme config
|
|
147
|
+
val typography = createCustomTypography(themeConfig)
|
|
148
|
+
|
|
149
|
+
// Apply theme with custom colors, logo, and typography
|
|
150
|
+
OctopusTheme(
|
|
151
|
+
colorScheme = finalColorScheme,
|
|
152
|
+
drawables = drawables,
|
|
153
|
+
typography = typography
|
|
59
154
|
) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
155
|
+
OctopusUIContent(onBack = onBack)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private fun createCustomTypography(themeConfig: OctopusThemeConfig?): OctopusTypography {
|
|
160
|
+
val defaultTypography = OctopusTypographyDefaults.typography()
|
|
161
|
+
|
|
162
|
+
if (themeConfig?.fonts == null) {
|
|
163
|
+
return defaultTypography
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
val fontsConfig = themeConfig.fonts!!
|
|
167
|
+
val textStyles = fontsConfig.textStyles
|
|
168
|
+
|
|
169
|
+
// Create custom typography based on new unified font configuration
|
|
170
|
+
if (textStyles != null && textStyles.isNotEmpty()) {
|
|
171
|
+
return OctopusTypographyDefaults.typography(
|
|
172
|
+
title1 = createTextStyle(textStyles["title1"], defaultTypography.title1),
|
|
173
|
+
title2 = createTextStyle(textStyles["title2"], defaultTypography.title2),
|
|
174
|
+
body1 = createTextStyle(textStyles["body1"], defaultTypography.body1),
|
|
175
|
+
body2 = createTextStyle(textStyles["body2"], defaultTypography.body2),
|
|
176
|
+
caption1 = createTextStyle(textStyles["caption1"], defaultTypography.caption1),
|
|
177
|
+
caption2 = createTextStyle(textStyles["caption2"], defaultTypography.caption2)
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
return defaultTypography
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private fun createTextStyle(textStyleConfig: OctopusTextStyleConfig?, defaultStyle: TextStyle): TextStyle {
|
|
186
|
+
if (textStyleConfig == null) {
|
|
187
|
+
return defaultStyle
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
val fontFamily = when (textStyleConfig.fontType) {
|
|
191
|
+
"serif" -> FontFamily.Serif
|
|
192
|
+
"monospace" -> FontFamily.Monospace
|
|
193
|
+
"default" -> FontFamily.Default
|
|
194
|
+
else -> FontFamily.Default
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
val fontSize = textStyleConfig.fontSize?.let {
|
|
198
|
+
// Use points directly as sp (1 point ≈ 1 sp on Android)
|
|
199
|
+
it.sp
|
|
200
|
+
} ?: defaultStyle.fontSize
|
|
201
|
+
|
|
202
|
+
return defaultStyle.copy(
|
|
203
|
+
fontFamily = fontFamily,
|
|
204
|
+
fontSize = fontSize
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@Composable
|
|
210
|
+
private fun OctopusUIContent(onBack: () -> Unit) {
|
|
211
|
+
Box(modifier = Modifier.fillMaxSize()) {
|
|
212
|
+
val navController = rememberNavController()
|
|
213
|
+
|
|
214
|
+
NavHost(
|
|
215
|
+
navController = navController,
|
|
216
|
+
startDestination = "OctopusHome"
|
|
217
|
+
) {
|
|
218
|
+
composable(route = "OctopusHome") {
|
|
219
|
+
OctopusNavigationHandler(
|
|
220
|
+
navigateToLogin = {
|
|
221
|
+
OctopusEventEmitter.instance?.emitLoginRequired()
|
|
222
|
+
},
|
|
223
|
+
navigateToProfileEdit = { fieldToEdit ->
|
|
224
|
+
OctopusEventEmitter.instance?.emitEditUser(fieldToEdit)
|
|
225
|
+
},
|
|
226
|
+
navigateToClientObject = {
|
|
227
|
+
// TODO : Bridge Post react
|
|
228
|
+
}
|
|
229
|
+
) {
|
|
230
|
+
OctopusHomeScreen(
|
|
231
|
+
navController = navController,
|
|
232
|
+
backIcon = true,
|
|
233
|
+
onBack = onBack
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
octopusComposables(
|
|
238
|
+
navController = navController,
|
|
239
|
+
onNavigateToLogin = {
|
|
240
|
+
OctopusEventEmitter.instance?.emitLoginRequired()
|
|
241
|
+
},
|
|
242
|
+
onNavigateToProfileEdit = { fieldToEdit ->
|
|
243
|
+
OctopusEventEmitter.instance?.emitEditUser(fieldToEdit)
|
|
244
|
+
}
|
|
245
|
+
)
|
|
67
246
|
}
|
|
68
|
-
)
|
|
69
247
|
}
|
|
70
|
-
}
|
|
71
248
|
}
|
|
249
|
+
|
|
250
|
+
private suspend fun loadImageFromUri(uri: String): Painter? {
|
|
251
|
+
return withContext(Dispatchers.IO) {
|
|
252
|
+
try {
|
|
253
|
+
val url = URL(uri)
|
|
254
|
+
val inputStream = url.openStream()
|
|
255
|
+
val bitmap = BitmapFactory.decodeStream(inputStream)
|
|
256
|
+
inputStream.close()
|
|
257
|
+
|
|
258
|
+
if (bitmap != null) {
|
|
259
|
+
BitmapPainter(bitmap.asImageBitmap())
|
|
260
|
+
} else {
|
|
261
|
+
null
|
|
262
|
+
}
|
|
263
|
+
} catch (e: Exception) {
|
|
264
|
+
null
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// A utility class for creating UIColor instances from hex strings.
|
|
4
|
+
/// This avoids conflicts with other modules that might also extend UIColor.
|
|
5
|
+
internal class OctopusColorUtility {
|
|
6
|
+
|
|
7
|
+
/// Creates a UIColor from a hex string.
|
|
8
|
+
/// - Parameter hex: A hex color string (e.g., "#FF0000", "FF0000", "#FFF", etc.)
|
|
9
|
+
/// - Returns: A UIColor instance, or nil if the hex string is invalid
|
|
10
|
+
static func color(fromHex hex: String) -> UIColor? {
|
|
11
|
+
// Remove any whitespace and convert to lowercase
|
|
12
|
+
let hex = hex.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
|
13
|
+
|
|
14
|
+
// Remove # prefix if present
|
|
15
|
+
let cleanHex = hex.hasPrefix("#") ? String(hex.dropFirst()) : hex
|
|
16
|
+
|
|
17
|
+
// Validate hex string contains only valid characters
|
|
18
|
+
guard cleanHex.range(of: "^[0-9a-f]+$", options: .regularExpression) != nil else {
|
|
19
|
+
return nil
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var int: UInt64 = 0
|
|
23
|
+
Scanner(string: cleanHex).scanHexInt64(&int)
|
|
24
|
+
|
|
25
|
+
let a, r, g, b: UInt64
|
|
26
|
+
switch cleanHex.count {
|
|
27
|
+
case 3: // RGB (12-bit)
|
|
28
|
+
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
|
|
29
|
+
case 6: // RGB (24-bit)
|
|
30
|
+
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
|
|
31
|
+
case 8: // ARGB (32-bit)
|
|
32
|
+
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
|
|
33
|
+
default:
|
|
34
|
+
return nil
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return UIColor(
|
|
38
|
+
red: CGFloat(r) / 255,
|
|
39
|
+
green: CGFloat(g) / 255,
|
|
40
|
+
blue: CGFloat(b) / 255,
|
|
41
|
+
alpha: CGFloat(a) / 255
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -29,6 +29,14 @@ RCT_EXTERN_METHOD(cancelUserTokenRequest:(NSString *)requestId
|
|
|
29
29
|
RCT_EXTERN_METHOD(disconnectUser:(RCTPromiseResolveBlock)resolve
|
|
30
30
|
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
31
31
|
|
|
32
|
+
RCT_EXTERN_METHOD(updateColorScheme:(NSString *)colorScheme
|
|
33
|
+
withResolver:(RCTPromiseResolveBlock)resolve
|
|
34
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
35
|
+
|
|
36
|
+
RCT_EXTERN_METHOD(updateTheme:(NSDictionary *)themeOptions
|
|
37
|
+
withResolver:(RCTPromiseResolveBlock)resolve
|
|
38
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
39
|
+
|
|
32
40
|
+ (BOOL)requiresMainQueueSetup
|
|
33
41
|
{
|
|
34
42
|
return NO;
|
|
@@ -5,37 +5,43 @@ import UIKit
|
|
|
5
5
|
|
|
6
6
|
@objc(OctopusReactNativeSdk)
|
|
7
7
|
class OctopusReactNativeSdk: RCTEventEmitter {
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
// MARK: - Properties
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
private var octopusSDK: OctopusSDK?
|
|
12
12
|
private lazy var uiManager = OctopusUIManager()
|
|
13
13
|
private lazy var eventManager = OctopusEventManager(eventEmitter: self)
|
|
14
14
|
private let sdkInitializer = OctopusSDKInitializer()
|
|
15
15
|
private var ssoAuthenticator: OctopusSSOAuthenticator?
|
|
16
|
-
|
|
16
|
+
private var theme: OctopusTheme?
|
|
17
|
+
private var logoSource: [String: Any]?
|
|
18
|
+
private var fontConfiguration: [String: Any]?
|
|
19
|
+
|
|
17
20
|
// MARK: - Initialization
|
|
18
|
-
|
|
21
|
+
|
|
19
22
|
@objc(initialize:withResolver:withRejecter:)
|
|
20
23
|
func initialize(options: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
21
24
|
do {
|
|
22
25
|
self.octopusSDK = try sdkInitializer.initialize(options: options, eventManager: eventManager)
|
|
23
26
|
self.ssoAuthenticator = OctopusSSOAuthenticator(octopusSDK: self.octopusSDK!, eventManager: eventManager)
|
|
27
|
+
self.theme = sdkInitializer.parseTheme(from: options)
|
|
28
|
+
self.logoSource = sdkInitializer.getLogoSource(from: options)
|
|
29
|
+
self.fontConfiguration = sdkInitializer.getFontConfiguration(from: options)
|
|
24
30
|
resolve(nil)
|
|
25
31
|
} catch {
|
|
26
32
|
reject("INITIALIZE_ERROR", "Failed to initialize Octopus SDK: \(error.localizedDescription)", error)
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
|
-
|
|
35
|
+
|
|
30
36
|
// MARK: - User authentication
|
|
31
|
-
|
|
37
|
+
|
|
32
38
|
@objc(connectUser:withResolver:withRejecter:)
|
|
33
39
|
func connectUser(params: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
34
40
|
guard let authenticator = ssoAuthenticator else {
|
|
35
41
|
reject("CONNECT_USER_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
36
42
|
return
|
|
37
43
|
}
|
|
38
|
-
|
|
44
|
+
|
|
39
45
|
Task {
|
|
40
46
|
do {
|
|
41
47
|
try await authenticator.connectUser(params: params)
|
|
@@ -44,66 +50,66 @@ class OctopusReactNativeSdk: RCTEventEmitter {
|
|
|
44
50
|
reject("CONNECT_USER_ERROR", "Failed to connect user: \(error.localizedDescription)", error)
|
|
45
51
|
}
|
|
46
52
|
}
|
|
47
|
-
|
|
53
|
+
|
|
48
54
|
}
|
|
49
|
-
|
|
55
|
+
|
|
50
56
|
@objc(completeUserTokenRequest:withToken:withResolver:withRejecter:)
|
|
51
57
|
func completeUserTokenRequest(requestId: String, token: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
52
58
|
guard let authenticator = ssoAuthenticator else {
|
|
53
59
|
reject("PROVIDE_TOKEN_ERROR", "SDK not initialized", nil)
|
|
54
60
|
return
|
|
55
61
|
}
|
|
56
|
-
|
|
62
|
+
|
|
57
63
|
authenticator.completeTokenRequest(requestId: requestId, token: token)
|
|
58
64
|
resolve(nil)
|
|
59
65
|
}
|
|
60
|
-
|
|
66
|
+
|
|
61
67
|
@objc(cancelUserTokenRequest:withResolver:withRejecter:)
|
|
62
68
|
func cancelUserTokenRequest(requestId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
63
69
|
guard let authenticator = ssoAuthenticator else {
|
|
64
70
|
reject("PROVIDE_TOKEN_ERROR", "SDK not initialized", nil)
|
|
65
71
|
return
|
|
66
72
|
}
|
|
67
|
-
|
|
73
|
+
|
|
68
74
|
authenticator.cancelTokenRequest(requestId: requestId)
|
|
69
75
|
resolve(nil)
|
|
70
76
|
}
|
|
71
|
-
|
|
77
|
+
|
|
72
78
|
@objc(disconnectUser:withRejecter:)
|
|
73
79
|
func disconnectUser(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
74
80
|
guard let authenticator = ssoAuthenticator else {
|
|
75
81
|
reject("DISCONNECT_USER_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
76
82
|
return
|
|
77
83
|
}
|
|
78
|
-
|
|
84
|
+
|
|
79
85
|
do {
|
|
80
86
|
try authenticator.disconnectUser()
|
|
81
87
|
resolve(nil)
|
|
82
88
|
} catch {
|
|
83
89
|
reject("DISCONNECT_USER_ERROR", "Failed to disconnect user: \(error.localizedDescription)", error)
|
|
84
90
|
}
|
|
85
|
-
|
|
91
|
+
|
|
86
92
|
}
|
|
87
|
-
|
|
93
|
+
|
|
88
94
|
// MARK: - UI management
|
|
89
|
-
|
|
95
|
+
|
|
90
96
|
@objc(openUI:withRejecter:)
|
|
91
97
|
func openUI(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
92
98
|
guard let octopus = octopusSDK else {
|
|
93
99
|
reject("OPEN_UI_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
94
100
|
return
|
|
95
101
|
}
|
|
96
|
-
|
|
102
|
+
|
|
97
103
|
DispatchQueue.main.async {
|
|
98
104
|
do {
|
|
99
|
-
try self.uiManager.openUI(octopus: octopus)
|
|
105
|
+
try self.uiManager.openUI(octopus: octopus, theme: self.theme, logoSource: self.logoSource, fontConfiguration: self.fontConfiguration)
|
|
100
106
|
resolve(nil)
|
|
101
107
|
} catch {
|
|
102
108
|
reject("OPEN_UI_ERROR", error.localizedDescription, error)
|
|
103
109
|
}
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
|
-
|
|
112
|
+
|
|
107
113
|
@objc(closeUI:withRejecter:)
|
|
108
114
|
func closeUI(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
109
115
|
DispatchQueue.main.async {
|
|
@@ -115,33 +121,51 @@ class OctopusReactNativeSdk: RCTEventEmitter {
|
|
|
115
121
|
}
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
|
-
|
|
124
|
+
|
|
125
|
+
@objc(updateColorScheme:withResolver:withRejecter:)
|
|
126
|
+
func updateColorScheme(colorScheme: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
127
|
+
// iOS uses adaptive colors that automatically respond to system appearance changes
|
|
128
|
+
// No manual updates needed - the theme is applied when UI opens
|
|
129
|
+
resolve(nil)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@objc(updateTheme:withResolver:withRejecter:)
|
|
133
|
+
func updateTheme(themeOptions: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
134
|
+
do {
|
|
135
|
+
self.theme = sdkInitializer.parseTheme(from: themeOptions)
|
|
136
|
+
resolve(nil)
|
|
137
|
+
} catch {
|
|
138
|
+
reject("UPDATE_THEME_ERROR", "Failed to update theme: \(error.localizedDescription)", error)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
119
143
|
// MARK: - Lifecycle management
|
|
120
|
-
|
|
144
|
+
|
|
121
145
|
deinit {
|
|
122
146
|
cleanup()
|
|
123
147
|
}
|
|
124
|
-
|
|
148
|
+
|
|
125
149
|
private func cleanup() {
|
|
126
150
|
uiManager.cleanup()
|
|
127
151
|
octopusSDK = nil
|
|
128
152
|
}
|
|
129
|
-
|
|
153
|
+
|
|
130
154
|
@objc override func invalidate() {
|
|
131
155
|
cleanup()
|
|
132
156
|
super.invalidate()
|
|
133
157
|
}
|
|
134
|
-
|
|
158
|
+
|
|
135
159
|
// MARK: - RCTEventEmitter overrides
|
|
136
|
-
|
|
160
|
+
|
|
137
161
|
@objc override func supportedEvents() -> [String]! {
|
|
138
162
|
return eventManager.supportedEvents()
|
|
139
163
|
}
|
|
140
|
-
|
|
164
|
+
|
|
141
165
|
@objc override func startObserving() {
|
|
142
166
|
eventManager.startObserving()
|
|
143
167
|
}
|
|
144
|
-
|
|
168
|
+
|
|
145
169
|
@objc override func stopObserving() {
|
|
146
170
|
eventManager.stopObserving()
|
|
147
171
|
}
|