@octopus-community/react-native 1.0.0 → 1.0.2
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 +62 -2
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt +72 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeConfig.kt +11 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeManager.kt +11 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt +156 -39
- package/ios/OctopusColorUtility.swift +44 -0
- package/ios/OctopusReactNativeSdk.swift +32 -28
- package/ios/OctopusSDKInitializer.swift +65 -6
- package/ios/OctopusUIManager.swift +69 -8
- package/lib/module/initialize.js +4 -0
- package/lib/module/initialize.js.map +1 -1
- package/lib/typescript/src/initialize.d.ts +24 -0
- package/lib/typescript/src/initialize.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/initialize.ts +25 -0
package/README.md
CHANGED
|
@@ -49,7 +49,18 @@ Due to a bug in XCode 15, you might need to set `ENABLE_USER_SCRIPT_SANDBOXING`
|
|
|
49
49
|
|
|
50
50
|
### Expo
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Configure `use_frameworks` (static or dynamic) with `expo-build-properties`:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
[
|
|
56
|
+
"expo-build-properties",
|
|
57
|
+
{
|
|
58
|
+
"ios": {
|
|
59
|
+
"useFrameworks": "static"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
```
|
|
53
64
|
|
|
54
65
|
## Usage
|
|
55
66
|
|
|
@@ -89,12 +100,23 @@ import {
|
|
|
89
100
|
closeUI,
|
|
90
101
|
} from '@octopus-community/react-native';
|
|
91
102
|
|
|
92
|
-
// Initialize with SSO mode
|
|
103
|
+
// Initialize with SSO mode and custom theme
|
|
93
104
|
await initialize({
|
|
94
105
|
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
95
106
|
connectionMode: {
|
|
96
107
|
type: 'sso',
|
|
97
108
|
appManagedFields: ['username', 'profilePicture', 'biography']
|
|
109
|
+
},
|
|
110
|
+
theme: {
|
|
111
|
+
colors: {
|
|
112
|
+
primary: '#FF6B35', // Your brand's primary color
|
|
113
|
+
primaryLowContrast: '#FF8C69', // Lighter variation
|
|
114
|
+
primaryHighContrast: '#CC4A1A', // Darker variation
|
|
115
|
+
onPrimary: '#FFFFFF', // Text color on primary background
|
|
116
|
+
},
|
|
117
|
+
logo: {
|
|
118
|
+
image: Image.resolveAssetSource(require('./assets/logo.png')), // Your custom logo
|
|
119
|
+
},
|
|
98
120
|
}
|
|
99
121
|
});
|
|
100
122
|
|
|
@@ -144,6 +166,44 @@ Show the Octopus home screen with the [`openUI()`](./docs/api/functions/openUI.m
|
|
|
144
166
|
|
|
145
167
|
Future versions of this React Native module will let you show the UI in your React components. Please reach out if you need this prioritized.
|
|
146
168
|
|
|
169
|
+
### Theme Customization
|
|
170
|
+
|
|
171
|
+
You can customize the Octopus UI to match your app's branding by providing a theme configuration during initialization:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { Image } from 'react-native';
|
|
175
|
+
|
|
176
|
+
await initialize({
|
|
177
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
178
|
+
connectionMode: { type: 'octopus' },
|
|
179
|
+
theme: {
|
|
180
|
+
colors: {
|
|
181
|
+
primary: '#FF6B35', // Main brand color
|
|
182
|
+
primaryLowContrast: '#FF8C69', // Lighter variation of primary
|
|
183
|
+
primaryHighContrast: '#CC4A1A', // Darker variation for high contrast
|
|
184
|
+
onPrimary: '#FFFFFF', // Text color on primary background
|
|
185
|
+
},
|
|
186
|
+
logo: {
|
|
187
|
+
// Use Image.resolveAssetSource() for bundled image assets
|
|
188
|
+
image: Image.resolveAssetSource(require('./assets/logo.png')),
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Supported customizations:**
|
|
195
|
+
- **Colors**: Primary, low contrast, high contrast, and text colors (iOS & Android)
|
|
196
|
+
- **Logo**: Custom logo using bundled images (iOS & Android)
|
|
197
|
+
- **Color Format**: Hex strings with or without # prefix (e.g., `#FF6B35` or `FF6B35`)
|
|
198
|
+
- **Supported Hex Formats**: 3-digit (`#F63`), 6-digit (`#FF6633`), 8-digit (`#FF6633FF`)
|
|
199
|
+
- **Zero Dependencies**: Implementation uses only built-in native APIs
|
|
200
|
+
|
|
201
|
+
**Logo Options:**
|
|
202
|
+
- **Bundled Images**: Use `Image.resolveAssetSource(require('./assets/logo.png'))` for local assets
|
|
203
|
+
- **Platform Support**: Both iOS and Android support local image resources
|
|
204
|
+
|
|
205
|
+
**Note**: All theme properties are optional. If not provided, the default Octopus theme will be used.
|
|
206
|
+
|
|
147
207
|
### API docs
|
|
148
208
|
|
|
149
209
|
For details about the Typescript API, head to the [API docs](./docs/api/README.md).
|
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt
CHANGED
|
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
5
5
|
import com.facebook.react.bridge.ReadableMap
|
|
6
6
|
import com.octopuscommunity.sdk.OctopusSDK
|
|
7
7
|
import com.octopuscommunity.sdk.domain.model.ConnectionMode
|
|
8
|
+
import android.graphics.Color
|
|
8
9
|
|
|
9
10
|
class OctopusSDKInitializer {
|
|
10
11
|
|
|
@@ -17,6 +18,11 @@ class OctopusSDKInitializer {
|
|
|
17
18
|
|
|
18
19
|
try {
|
|
19
20
|
val connectionMode = parseConnectionMode(options)
|
|
21
|
+
|
|
22
|
+
// Store theme configuration for later use in UI
|
|
23
|
+
val themeConfig = parseThemeConfig(options)
|
|
24
|
+
OctopusThemeManager.setThemeConfig(themeConfig)
|
|
25
|
+
|
|
20
26
|
OctopusSDK.initialize(
|
|
21
27
|
context = context,
|
|
22
28
|
apiKey = apiKey,
|
|
@@ -29,6 +35,72 @@ class OctopusSDKInitializer {
|
|
|
29
35
|
promise.reject("INITIALIZE_ERROR", "Failed to initialize Octopus SDK", e)
|
|
30
36
|
}
|
|
31
37
|
}
|
|
38
|
+
|
|
39
|
+
private fun parseThemeConfig(options: ReadableMap): OctopusThemeConfig? {
|
|
40
|
+
val themeMap = options.getMap("theme") ?: return null
|
|
41
|
+
|
|
42
|
+
val colorsMap = themeMap.getMap("colors")
|
|
43
|
+
val logoMap = themeMap.getMap("logo")
|
|
44
|
+
|
|
45
|
+
var primaryColor: String? = null
|
|
46
|
+
var primaryLowContrastColor: String? = null
|
|
47
|
+
var primaryHighContrastColor: String? = null
|
|
48
|
+
var onPrimaryColor: String? = null
|
|
49
|
+
var logoSource: ReadableMap? = null
|
|
50
|
+
|
|
51
|
+
colorsMap?.let { colors ->
|
|
52
|
+
primaryColor = parseColor(colors.getString("primary"))
|
|
53
|
+
primaryLowContrastColor = parseColor(colors.getString("primaryLowContrast"))
|
|
54
|
+
primaryHighContrastColor = parseColor(colors.getString("primaryHighContrast"))
|
|
55
|
+
onPrimaryColor = parseColor(colors.getString("onPrimary"))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Handle logo - only support Image.resolveAssetSource() approach
|
|
59
|
+
logoMap?.let { logo ->
|
|
60
|
+
val imageSource = logo.getMap("image")
|
|
61
|
+
if (imageSource != null) {
|
|
62
|
+
logoSource = imageSource
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Only create theme config if we have at least one customization
|
|
67
|
+
if (primaryColor != null || logoSource != null) {
|
|
68
|
+
return OctopusThemeConfig(
|
|
69
|
+
primaryColor = primaryColor,
|
|
70
|
+
primaryLowContrastColor = primaryLowContrastColor,
|
|
71
|
+
primaryHighContrastColor = primaryHighContrastColor,
|
|
72
|
+
onPrimaryColor = onPrimaryColor,
|
|
73
|
+
logoSource = logoSource
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun extractResourceNameFromUri(uri: String): String? {
|
|
81
|
+
// Extract resource name from React Native image URI
|
|
82
|
+
// Examples: "logo.png" -> "logo", "images/logo.png" -> "logo"
|
|
83
|
+
return try {
|
|
84
|
+
val fileName = uri.substringAfterLast("/")
|
|
85
|
+
fileName.substringBeforeLast(".")
|
|
86
|
+
} catch (e: Exception) {
|
|
87
|
+
null
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private fun parseColor(colorString: String?): String? {
|
|
92
|
+
if (colorString == null) return null
|
|
93
|
+
|
|
94
|
+
return try {
|
|
95
|
+
// Validate that the color string is a valid hex color
|
|
96
|
+
Color.parseColor(colorString)
|
|
97
|
+
// Return the original string if parsing succeeds
|
|
98
|
+
colorString
|
|
99
|
+
} catch (e: IllegalArgumentException) {
|
|
100
|
+
// Invalid color format - return null to skip this color
|
|
101
|
+
null
|
|
102
|
+
}
|
|
103
|
+
}
|
|
32
104
|
|
|
33
105
|
private fun parseConnectionMode(options: ReadableMap): ConnectionMode {
|
|
34
106
|
val connectionModeMap = options.getMap("connectionMode")
|
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeConfig.kt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package com.octopuscommunity.octopusreactnativesdk
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap
|
|
4
|
+
|
|
5
|
+
data class OctopusThemeConfig(
|
|
6
|
+
val primaryColor: String?,
|
|
7
|
+
val primaryLowContrastColor: String?,
|
|
8
|
+
val primaryHighContrastColor: String?,
|
|
9
|
+
val onPrimaryColor: String?,
|
|
10
|
+
val logoSource: ReadableMap?
|
|
11
|
+
)
|
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeManager.kt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package com.octopuscommunity.octopusreactnativesdk
|
|
2
|
+
|
|
3
|
+
object OctopusThemeManager {
|
|
4
|
+
private var themeConfig: OctopusThemeConfig? = null
|
|
5
|
+
|
|
6
|
+
fun setThemeConfig(config: OctopusThemeConfig?) {
|
|
7
|
+
themeConfig = config
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
fun getThemeConfig(): OctopusThemeConfig? = themeConfig
|
|
11
|
+
}
|
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt
CHANGED
|
@@ -4,68 +4,185 @@ 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.home.OctopusHomeScreen
|
|
18
34
|
import com.octopuscommunity.sdk.ui.octopusComposables
|
|
35
|
+
import com.octopuscommunity.sdk.ui.octopusDarkColorScheme
|
|
36
|
+
import com.octopuscommunity.sdk.ui.octopusLightColorScheme
|
|
37
|
+
import kotlinx.coroutines.Dispatchers
|
|
38
|
+
import kotlinx.coroutines.withContext
|
|
39
|
+
import java.net.URL
|
|
19
40
|
|
|
20
41
|
class OctopusUIActivity : ComponentActivity() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
42
|
+
private val closeUIReceiver = object : BroadcastReceiver() {
|
|
43
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
44
|
+
finish()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
49
|
+
super.onCreate(savedInstanceState)
|
|
50
|
+
registerCloseUIReceiver()
|
|
51
|
+
setContent {
|
|
52
|
+
OctopusUI(onBack = { finish() })
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override fun onDestroy() {
|
|
57
|
+
super.onDestroy()
|
|
58
|
+
unregisterReceiver(closeUIReceiver)
|
|
24
59
|
}
|
|
25
|
-
}
|
|
26
60
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
61
|
+
private fun registerCloseUIReceiver() {
|
|
62
|
+
val intentFilter = IntentFilter(OctopusUIController.CLOSE_UI_ACTION)
|
|
63
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
64
|
+
registerReceiver(closeUIReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
65
|
+
} else {
|
|
66
|
+
@Suppress("UnspecifiedRegisterReceiverFlag")
|
|
67
|
+
registerReceiver(closeUIReceiver, intentFilter)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Composable
|
|
73
|
+
private fun OctopusUI(onBack: () -> Unit) {
|
|
74
|
+
val themeConfig = OctopusThemeManager.getThemeConfig()
|
|
75
|
+
val context = LocalContext.current
|
|
76
|
+
|
|
77
|
+
// Function to detect if system is in dark mode
|
|
78
|
+
fun isSystemInDarkTheme(): Boolean {
|
|
79
|
+
return (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
|
32
80
|
}
|
|
33
|
-
}
|
|
34
81
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
82
|
+
// Only apply custom theming if theme config is provided
|
|
83
|
+
if (themeConfig != null) {
|
|
84
|
+
// Create color scheme based on theme config
|
|
85
|
+
val colorScheme = if (isSystemInDarkTheme()) {
|
|
86
|
+
octopusDarkColorScheme()
|
|
87
|
+
} else {
|
|
88
|
+
octopusLightColorScheme()
|
|
89
|
+
}.let { octopusColorScheme ->
|
|
90
|
+
octopusColorScheme.copy(
|
|
91
|
+
primary = themeConfig.primaryColor?.let {
|
|
92
|
+
Color(android.graphics.Color.parseColor(it))
|
|
93
|
+
} ?: octopusColorScheme.primary,
|
|
94
|
+
primaryLow = themeConfig.primaryLowContrastColor?.let {
|
|
95
|
+
Color(android.graphics.Color.parseColor(it))
|
|
96
|
+
} ?: octopusColorScheme.primaryLow,
|
|
97
|
+
primaryHigh = themeConfig.primaryHighContrastColor?.let {
|
|
98
|
+
Color(android.graphics.Color.parseColor(it))
|
|
99
|
+
} ?: octopusColorScheme.primaryHigh,
|
|
100
|
+
onPrimary = themeConfig.onPrimaryColor?.let {
|
|
101
|
+
Color(android.graphics.Color.parseColor(it))
|
|
102
|
+
} ?: octopusColorScheme.onPrimary
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Handle logo loading using built-in Android capabilities
|
|
107
|
+
var logoPainter by remember { mutableStateOf<Painter?>(null) }
|
|
108
|
+
|
|
109
|
+
LaunchedEffect(themeConfig.logoSource) {
|
|
110
|
+
themeConfig.logoSource?.let { logoSource ->
|
|
111
|
+
val uri = logoSource.getString("uri")
|
|
112
|
+
if (uri != null) {
|
|
113
|
+
logoPainter = loadImageFromUri(uri)
|
|
114
|
+
} else {
|
|
115
|
+
logoPainter = null
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Create drawables based on theme config
|
|
121
|
+
val drawables = if (logoPainter != null) {
|
|
122
|
+
OctopusDrawablesDefaults.drawables(logo = logoPainter)
|
|
123
|
+
} else {
|
|
124
|
+
OctopusDrawablesDefaults.drawables()
|
|
125
|
+
}
|
|
39
126
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
127
|
+
// Apply custom theme
|
|
128
|
+
OctopusTheme(
|
|
129
|
+
colorScheme = colorScheme,
|
|
130
|
+
drawables = drawables
|
|
131
|
+
) {
|
|
132
|
+
OctopusUIContent(onBack = onBack)
|
|
133
|
+
}
|
|
44
134
|
} else {
|
|
45
|
-
|
|
46
|
-
|
|
135
|
+
// No theme config - let the native SDK inherit the React Native app's theme colors
|
|
136
|
+
OctopusUIContent(onBack = onBack)
|
|
47
137
|
}
|
|
48
|
-
}
|
|
49
138
|
}
|
|
50
139
|
|
|
51
140
|
@Composable
|
|
52
|
-
private fun
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
141
|
+
private fun OctopusUIContent(onBack: () -> Unit) {
|
|
142
|
+
Box(modifier = Modifier.fillMaxSize()) {
|
|
143
|
+
val navController = rememberNavController()
|
|
144
|
+
|
|
145
|
+
NavHost(
|
|
146
|
+
navController = navController,
|
|
147
|
+
startDestination = "OctopusHome"
|
|
148
|
+
) {
|
|
149
|
+
composable(route = "OctopusHome") {
|
|
150
|
+
OctopusHomeScreen(
|
|
151
|
+
navController = navController,
|
|
152
|
+
backIcon = true,
|
|
153
|
+
onBack = onBack
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
octopusComposables(
|
|
157
|
+
navController = navController,
|
|
158
|
+
onNavigateToLogin = {
|
|
159
|
+
OctopusEventEmitter.instance?.emitLoginRequired()
|
|
160
|
+
},
|
|
161
|
+
onNavigateToProfileEdit = { fieldToEdit ->
|
|
162
|
+
OctopusEventEmitter.instance?.emitEditUser(fieldToEdit)
|
|
163
|
+
}
|
|
164
|
+
)
|
|
67
165
|
}
|
|
68
|
-
)
|
|
69
166
|
}
|
|
70
|
-
}
|
|
71
167
|
}
|
|
168
|
+
|
|
169
|
+
private suspend fun loadImageFromUri(uri: String): Painter? {
|
|
170
|
+
return withContext(Dispatchers.IO) {
|
|
171
|
+
try {
|
|
172
|
+
val url = URL(uri)
|
|
173
|
+
val inputStream = url.openStream()
|
|
174
|
+
val bitmap = BitmapFactory.decodeStream(inputStream)
|
|
175
|
+
inputStream.close()
|
|
176
|
+
|
|
177
|
+
if (bitmap != null) {
|
|
178
|
+
BitmapPainter(bitmap.asImageBitmap())
|
|
179
|
+
} else {
|
|
180
|
+
Log.w("OctopusUI", "Failed to decode bitmap from URI: $uri")
|
|
181
|
+
null
|
|
182
|
+
}
|
|
183
|
+
} catch (e: Exception) {
|
|
184
|
+
Log.e("OctopusUI", "Error loading image from URI: $uri", e)
|
|
185
|
+
null
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -5,37 +5,41 @@ 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
|
+
|
|
17
19
|
// MARK: - Initialization
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
@objc(initialize:withResolver:withRejecter:)
|
|
20
22
|
func initialize(options: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
21
23
|
do {
|
|
22
24
|
self.octopusSDK = try sdkInitializer.initialize(options: options, eventManager: eventManager)
|
|
23
25
|
self.ssoAuthenticator = OctopusSSOAuthenticator(octopusSDK: self.octopusSDK!, eventManager: eventManager)
|
|
26
|
+
self.theme = sdkInitializer.parseTheme(from: options)
|
|
27
|
+
self.logoSource = sdkInitializer.getLogoSource(from: options)
|
|
24
28
|
resolve(nil)
|
|
25
29
|
} catch {
|
|
26
30
|
reject("INITIALIZE_ERROR", "Failed to initialize Octopus SDK: \(error.localizedDescription)", error)
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
|
-
|
|
33
|
+
|
|
30
34
|
// MARK: - User authentication
|
|
31
|
-
|
|
35
|
+
|
|
32
36
|
@objc(connectUser:withResolver:withRejecter:)
|
|
33
37
|
func connectUser(params: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
34
38
|
guard let authenticator = ssoAuthenticator else {
|
|
35
39
|
reject("CONNECT_USER_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
36
40
|
return
|
|
37
41
|
}
|
|
38
|
-
|
|
42
|
+
|
|
39
43
|
Task {
|
|
40
44
|
do {
|
|
41
45
|
try await authenticator.connectUser(params: params)
|
|
@@ -44,66 +48,66 @@ class OctopusReactNativeSdk: RCTEventEmitter {
|
|
|
44
48
|
reject("CONNECT_USER_ERROR", "Failed to connect user: \(error.localizedDescription)", error)
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
|
-
|
|
51
|
+
|
|
48
52
|
}
|
|
49
|
-
|
|
53
|
+
|
|
50
54
|
@objc(completeUserTokenRequest:withToken:withResolver:withRejecter:)
|
|
51
55
|
func completeUserTokenRequest(requestId: String, token: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
52
56
|
guard let authenticator = ssoAuthenticator else {
|
|
53
57
|
reject("PROVIDE_TOKEN_ERROR", "SDK not initialized", nil)
|
|
54
58
|
return
|
|
55
59
|
}
|
|
56
|
-
|
|
60
|
+
|
|
57
61
|
authenticator.completeTokenRequest(requestId: requestId, token: token)
|
|
58
62
|
resolve(nil)
|
|
59
63
|
}
|
|
60
|
-
|
|
64
|
+
|
|
61
65
|
@objc(cancelUserTokenRequest:withResolver:withRejecter:)
|
|
62
66
|
func cancelUserTokenRequest(requestId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
63
67
|
guard let authenticator = ssoAuthenticator else {
|
|
64
68
|
reject("PROVIDE_TOKEN_ERROR", "SDK not initialized", nil)
|
|
65
69
|
return
|
|
66
70
|
}
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
authenticator.cancelTokenRequest(requestId: requestId)
|
|
69
73
|
resolve(nil)
|
|
70
74
|
}
|
|
71
|
-
|
|
75
|
+
|
|
72
76
|
@objc(disconnectUser:withRejecter:)
|
|
73
77
|
func disconnectUser(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
74
78
|
guard let authenticator = ssoAuthenticator else {
|
|
75
79
|
reject("DISCONNECT_USER_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
76
80
|
return
|
|
77
81
|
}
|
|
78
|
-
|
|
82
|
+
|
|
79
83
|
do {
|
|
80
84
|
try authenticator.disconnectUser()
|
|
81
85
|
resolve(nil)
|
|
82
86
|
} catch {
|
|
83
87
|
reject("DISCONNECT_USER_ERROR", "Failed to disconnect user: \(error.localizedDescription)", error)
|
|
84
88
|
}
|
|
85
|
-
|
|
89
|
+
|
|
86
90
|
}
|
|
87
|
-
|
|
91
|
+
|
|
88
92
|
// MARK: - UI management
|
|
89
|
-
|
|
93
|
+
|
|
90
94
|
@objc(openUI:withRejecter:)
|
|
91
95
|
func openUI(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
92
96
|
guard let octopus = octopusSDK else {
|
|
93
97
|
reject("OPEN_UI_ERROR", "SDK not initialized. Call initialize() first.", nil)
|
|
94
98
|
return
|
|
95
99
|
}
|
|
96
|
-
|
|
100
|
+
|
|
97
101
|
DispatchQueue.main.async {
|
|
98
102
|
do {
|
|
99
|
-
try self.uiManager.openUI(octopus: octopus)
|
|
103
|
+
try self.uiManager.openUI(octopus: octopus, theme: self.theme, logoSource: self.logoSource)
|
|
100
104
|
resolve(nil)
|
|
101
105
|
} catch {
|
|
102
106
|
reject("OPEN_UI_ERROR", error.localizedDescription, error)
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
|
-
|
|
110
|
+
|
|
107
111
|
@objc(closeUI:withRejecter:)
|
|
108
112
|
func closeUI(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
109
113
|
DispatchQueue.main.async {
|
|
@@ -115,33 +119,33 @@ class OctopusReactNativeSdk: RCTEventEmitter {
|
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
|
-
|
|
122
|
+
|
|
119
123
|
// MARK: - Lifecycle management
|
|
120
|
-
|
|
124
|
+
|
|
121
125
|
deinit {
|
|
122
126
|
cleanup()
|
|
123
127
|
}
|
|
124
|
-
|
|
128
|
+
|
|
125
129
|
private func cleanup() {
|
|
126
130
|
uiManager.cleanup()
|
|
127
131
|
octopusSDK = nil
|
|
128
132
|
}
|
|
129
|
-
|
|
133
|
+
|
|
130
134
|
@objc override func invalidate() {
|
|
131
135
|
cleanup()
|
|
132
136
|
super.invalidate()
|
|
133
137
|
}
|
|
134
|
-
|
|
138
|
+
|
|
135
139
|
// MARK: - RCTEventEmitter overrides
|
|
136
|
-
|
|
140
|
+
|
|
137
141
|
@objc override func supportedEvents() -> [String]! {
|
|
138
142
|
return eventManager.supportedEvents()
|
|
139
143
|
}
|
|
140
|
-
|
|
144
|
+
|
|
141
145
|
@objc override func startObserving() {
|
|
142
146
|
eventManager.startObserving()
|
|
143
147
|
}
|
|
144
|
-
|
|
148
|
+
|
|
145
149
|
@objc override func stopObserving() {
|
|
146
150
|
eventManager.stopObserving()
|
|
147
151
|
}
|
|
@@ -1,22 +1,81 @@
|
|
|
1
1
|
import Octopus
|
|
2
|
+
import OctopusUI
|
|
3
|
+
import SwiftUI
|
|
2
4
|
|
|
3
5
|
class OctopusSDKInitializer {
|
|
4
6
|
func initialize(options: [String: Any], eventManager: OctopusEventManager) throws -> OctopusSDK {
|
|
5
7
|
guard let apiKey = options["apiKey"] as? String else {
|
|
6
8
|
throw InitializationError.missingAPIKey
|
|
7
9
|
}
|
|
8
|
-
|
|
10
|
+
|
|
9
11
|
guard let connectionModeMap = options["connectionMode"] as? [String: Any] else {
|
|
10
12
|
throw InitializationError.missingConnectionMode
|
|
11
13
|
}
|
|
12
|
-
|
|
14
|
+
|
|
13
15
|
let connectionMode = try parseConnectionMode(from: connectionModeMap, eventManager: eventManager)
|
|
14
16
|
return try OctopusSDK(apiKey: apiKey, connectionMode: connectionMode)
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
func parseTheme(from options: [String: Any]) -> OctopusTheme? {
|
|
20
|
+
guard let themeMap = options["theme"] as? [String: Any] else {
|
|
21
|
+
return nil
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var colors: OctopusTheme.Colors?
|
|
25
|
+
|
|
26
|
+
// Parse colors
|
|
27
|
+
if let colorsMap = themeMap["colors"] as? [String: Any] {
|
|
28
|
+
var primarySet: OctopusTheme.Colors.ColorSet?
|
|
29
|
+
|
|
30
|
+
if let primary = colorsMap["primary"] as? String,
|
|
31
|
+
let primaryColor = OctopusColorUtility.color(fromHex: primary) {
|
|
32
|
+
let lowContrast = (colorsMap["primaryLowContrast"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? primaryColor
|
|
33
|
+
let highContrast = (colorsMap["primaryHighContrast"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? UIColor.black
|
|
34
|
+
let onPrimary = (colorsMap["onPrimary"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? UIColor.white
|
|
35
|
+
|
|
36
|
+
primarySet = OctopusTheme.Colors.ColorSet(
|
|
37
|
+
main: Color(uiColor: primaryColor),
|
|
38
|
+
lowContrast: Color(uiColor: lowContrast),
|
|
39
|
+
highContrast: Color(uiColor: highContrast)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
colors = OctopusTheme.Colors(
|
|
43
|
+
primarySet: primarySet,
|
|
44
|
+
onPrimary: Color(uiColor: onPrimary)
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Note: Logo will be handled separately in UI manager due to async loading requirements
|
|
50
|
+
// Only create theme if we have colors, otherwise return nil
|
|
51
|
+
guard let colors = colors else {
|
|
52
|
+
return nil
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return OctopusTheme(
|
|
56
|
+
colors: colors,
|
|
57
|
+
fonts: OctopusTheme.Fonts(),
|
|
58
|
+
assets: OctopusTheme.Assets()
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func getLogoSource(from options: [String: Any]) -> [String: Any]? {
|
|
63
|
+
guard let themeMap = options["theme"] as? [String: Any],
|
|
64
|
+
let logoMap = themeMap["logo"] as? [String: Any] else {
|
|
65
|
+
return nil
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Only support Image.resolveAssetSource() approach
|
|
69
|
+
if let imageSource = logoMap["image"] as? [String: Any] {
|
|
70
|
+
return imageSource
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return nil
|
|
74
|
+
}
|
|
75
|
+
|
|
17
76
|
private func parseConnectionMode(from connectionModeMap: [String: Any], eventManager: OctopusEventManager) throws -> ConnectionMode {
|
|
18
77
|
let connectionModeType = connectionModeMap["type"] as? String
|
|
19
|
-
|
|
78
|
+
|
|
20
79
|
switch connectionModeType {
|
|
21
80
|
case "sso":
|
|
22
81
|
return try createSSOConnectionMode(from: connectionModeMap, eventManager: eventManager)
|
|
@@ -26,10 +85,10 @@ class OctopusSDKInitializer {
|
|
|
26
85
|
throw InitializationError.invalidConnectionModeType
|
|
27
86
|
}
|
|
28
87
|
}
|
|
29
|
-
|
|
88
|
+
|
|
30
89
|
private func createSSOConnectionMode(from connectionModeMap: [String: Any], eventManager: OctopusEventManager) throws -> ConnectionMode {
|
|
31
90
|
let appManagedFields = ProfileFieldMapper.fromReactNativeArray(connectionModeMap["appManagedFields"] as? [String])
|
|
32
|
-
|
|
91
|
+
|
|
33
92
|
return .sso(
|
|
34
93
|
.init(
|
|
35
94
|
appManagedFields: appManagedFields,
|
|
@@ -48,7 +107,7 @@ enum InitializationError: Error, LocalizedError {
|
|
|
48
107
|
case missingAPIKey
|
|
49
108
|
case missingConnectionMode
|
|
50
109
|
case invalidConnectionModeType
|
|
51
|
-
|
|
110
|
+
|
|
52
111
|
var errorDescription: String? {
|
|
53
112
|
switch self {
|
|
54
113
|
case .missingAPIKey:
|
|
@@ -6,31 +6,92 @@ import React
|
|
|
6
6
|
|
|
7
7
|
class OctopusUIManager {
|
|
8
8
|
private weak var presentedViewController: UIViewController?
|
|
9
|
-
|
|
10
|
-
func openUI(octopus: OctopusSDK) throws {
|
|
9
|
+
|
|
10
|
+
func openUI(octopus: OctopusSDK, theme: OctopusUI.OctopusTheme?, logoSource: [String: Any]?) throws {
|
|
11
11
|
guard let presentingViewController = RCTPresentedViewController() else {
|
|
12
12
|
throw NSError(domain: "OPEN_UI_ERROR", code: 0, userInfo: [NSLocalizedDescriptionKey: "Could not find presenting view controller"])
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
let octopusHomeScreen = OctopusHomeScreen(octopus: octopus)
|
|
16
|
-
let hostingController = UIHostingController(rootView: octopusHomeScreen)
|
|
16
|
+
let hostingController = UIHostingController(rootView: AnyView(octopusHomeScreen))
|
|
17
17
|
hostingController.modalPresentationStyle = .fullScreen
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
// Apply theme if provided
|
|
20
|
+
if let theme = theme {
|
|
21
|
+
// Apply theme immediately (without logo) to avoid delay
|
|
22
|
+
hostingController.rootView = AnyView(OctopusHomeScreen(octopus: octopus).environment(\.octopusTheme, theme))
|
|
23
|
+
|
|
24
|
+
// Then load logo asynchronously and update theme if logo loads
|
|
25
|
+
if let logoSource = logoSource {
|
|
26
|
+
loadLogo(from: logoSource) { [weak hostingController] logoImage in
|
|
27
|
+
DispatchQueue.main.async {
|
|
28
|
+
if let logoImage = logoImage {
|
|
29
|
+
let updatedTheme = OctopusUI.OctopusTheme(
|
|
30
|
+
colors: theme.colors,
|
|
31
|
+
fonts: theme.fonts,
|
|
32
|
+
assets: OctopusUI.OctopusTheme.Assets(logo: logoImage)
|
|
33
|
+
)
|
|
34
|
+
hostingController?.rootView = AnyView(OctopusHomeScreen(octopus: octopus).environment(\.octopusTheme, updatedTheme))
|
|
35
|
+
} else {
|
|
36
|
+
// Theme is already applied, no need to do anything
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
19
43
|
presentedViewController = hostingController
|
|
20
|
-
|
|
44
|
+
|
|
21
45
|
presentingViewController.present(hostingController, animated: true)
|
|
22
46
|
}
|
|
23
47
|
|
|
48
|
+
private func loadLogo(from source: [String: Any], completion: @escaping (UIImage?) -> Void) {
|
|
49
|
+
// Handle React Native image source with URI (from Image.resolveAssetSource)
|
|
50
|
+
guard let uri = source["uri"] as? String else {
|
|
51
|
+
completion(nil)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Remote URL
|
|
56
|
+
if uri.hasPrefix("http") {
|
|
57
|
+
guard let url = URL(string: uri) else {
|
|
58
|
+
completion(nil)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
URLSession.shared.dataTask(with: url) { data, response, error in
|
|
63
|
+
guard let data = data, error == nil else {
|
|
64
|
+
completion(nil)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let image = UIImage(data: data)
|
|
69
|
+
completion(image)
|
|
70
|
+
}.resume()
|
|
71
|
+
} else {
|
|
72
|
+
// Local file path (from Image.resolveAssetSource)
|
|
73
|
+
if let image = UIImage(contentsOfFile: uri) {
|
|
74
|
+
completion(image)
|
|
75
|
+
} else {
|
|
76
|
+
// Fallback: try to load from bundle using the filename
|
|
77
|
+
let filename = (uri as NSString).lastPathComponent
|
|
78
|
+
let nameWithoutExtension = (filename as NSString).deletingPathExtension
|
|
79
|
+
let image = UIImage(named: nameWithoutExtension)
|
|
80
|
+
completion(image)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
24
85
|
func closeUI() throws {
|
|
25
86
|
guard let presentedVC = presentedViewController else {
|
|
26
87
|
throw NSError(domain: "CLOSE_UI_ERROR", code: 0, userInfo: [NSLocalizedDescriptionKey: "No UI is currently presented"])
|
|
27
88
|
}
|
|
28
|
-
|
|
89
|
+
|
|
29
90
|
presentedVC.dismiss(animated: true) {
|
|
30
91
|
self.presentedViewController = nil
|
|
31
92
|
}
|
|
32
93
|
}
|
|
33
|
-
|
|
94
|
+
|
|
34
95
|
func cleanup() {
|
|
35
96
|
if let presentedVC = presentedViewController {
|
|
36
97
|
presentedVC.dismiss(animated: false, completion: nil)
|
package/lib/module/initialize.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["OctopusReactNativeSdk","initialize","params"],"sourceRoot":"../../src","sources":["initialize.ts"],"mappings":";;AAAA,SAASA,qBAAqB,QAAQ,6BAA0B;;
|
|
1
|
+
{"version":3,"names":["OctopusReactNativeSdk","initialize","params"],"sourceRoot":"../../src","sources":["initialize.ts"],"mappings":";;AAAA,SAASA,qBAAqB,QAAQ,6BAA0B;;AAIhE;AACA;AACA;;AAoBA;AACA;AACA;;AAwBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAACC,MAAwB,EAAiB;EAClE,OAAOF,qBAAqB,CAACC,UAAU,CAACC,MAAM,CAAC;AACjD","ignoreList":[]}
|
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import type { UserProfileField } from './types/userProfileField';
|
|
2
|
+
import type { ImageResolvedAssetSource } from 'react-native';
|
|
3
|
+
/**
|
|
4
|
+
* Theme configuration for customizing the Octopus UI appearance.
|
|
5
|
+
*/
|
|
6
|
+
export interface OctopusTheme {
|
|
7
|
+
/** Color customization options */
|
|
8
|
+
colors?: {
|
|
9
|
+
/** Primary color set for branding (hex format: #FF6B35 or FF6B35) */
|
|
10
|
+
primary?: string;
|
|
11
|
+
/** Primary low contrast color (lighter variation of primary) (hex format: #FF6B35 or FF6B35) */
|
|
12
|
+
primaryLowContrast?: string;
|
|
13
|
+
/** High contrast variation of primary color (hex format: #FF6B35 or FF6B35) */
|
|
14
|
+
primaryHighContrast?: string;
|
|
15
|
+
/** Color for content displayed over the primary color (hex format: #FF6B35 or FF6B35) */
|
|
16
|
+
onPrimary?: string;
|
|
17
|
+
};
|
|
18
|
+
/** Logo customization */
|
|
19
|
+
logo?: {
|
|
20
|
+
/** Local image resource - use Image.resolveAssetSource(require('./path/to/image.png')) */
|
|
21
|
+
image?: ImageResolvedAssetSource;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
2
24
|
/**
|
|
3
25
|
* Configuration params for initializing the Octopus SDK.
|
|
4
26
|
*/
|
|
@@ -19,6 +41,8 @@ export interface InitializeParams {
|
|
|
19
41
|
/** Octopus-managed authentication mode */
|
|
20
42
|
type: 'octopus';
|
|
21
43
|
};
|
|
44
|
+
/** Optional theme customization for the Octopus UI */
|
|
45
|
+
theme?: OctopusTheme;
|
|
22
46
|
}
|
|
23
47
|
/**
|
|
24
48
|
* Initializes the Octopus SDK with the provided configuration.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../../../src/initialize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../../../src/initialize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,MAAM,CAAC,EAAE;QACP,qEAAqE;QACrE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gGAAgG;QAChG,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,+EAA+E;QAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,yFAAyF;QACzF,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,yBAAyB;IACzB,IAAI,CAAC,EAAE;QACL,0FAA0F;QAC1F,KAAK,CAAC,EAAE,wBAAwB,CAAC;KAClC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,cAAc,EACV;QACE,6BAA6B;QAC7B,IAAI,EAAE,KAAK,CAAC;QACZ,iEAAiE;QACjE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;KACtC,GACD;QACE,0CAA0C;QAC1C,IAAI,EAAE,SAAS,CAAC;KACjB,CAAC;IACN,sDAAsD;IACtD,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAElE"}
|
package/package.json
CHANGED
package/src/initialize.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import { OctopusReactNativeSdk } from './internals/nativeModule';
|
|
2
2
|
import type { UserProfileField } from './types/userProfileField';
|
|
3
|
+
import type { ImageResolvedAssetSource } from 'react-native';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Theme configuration for customizing the Octopus UI appearance.
|
|
7
|
+
*/
|
|
8
|
+
export interface OctopusTheme {
|
|
9
|
+
/** Color customization options */
|
|
10
|
+
colors?: {
|
|
11
|
+
/** Primary color set for branding (hex format: #FF6B35 or FF6B35) */
|
|
12
|
+
primary?: string;
|
|
13
|
+
/** Primary low contrast color (lighter variation of primary) (hex format: #FF6B35 or FF6B35) */
|
|
14
|
+
primaryLowContrast?: string;
|
|
15
|
+
/** High contrast variation of primary color (hex format: #FF6B35 or FF6B35) */
|
|
16
|
+
primaryHighContrast?: string;
|
|
17
|
+
/** Color for content displayed over the primary color (hex format: #FF6B35 or FF6B35) */
|
|
18
|
+
onPrimary?: string;
|
|
19
|
+
};
|
|
20
|
+
/** Logo customization */
|
|
21
|
+
logo?: {
|
|
22
|
+
/** Local image resource - use Image.resolveAssetSource(require('./path/to/image.png')) */
|
|
23
|
+
image?: ImageResolvedAssetSource;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
3
26
|
|
|
4
27
|
/**
|
|
5
28
|
* Configuration params for initializing the Octopus SDK.
|
|
@@ -23,6 +46,8 @@ export interface InitializeParams {
|
|
|
23
46
|
/** Octopus-managed authentication mode */
|
|
24
47
|
type: 'octopus';
|
|
25
48
|
};
|
|
49
|
+
/** Optional theme customization for the Octopus UI */
|
|
50
|
+
theme?: OctopusTheme;
|
|
26
51
|
}
|
|
27
52
|
|
|
28
53
|
/**
|