@octopus-community/react-native 1.0.6 → 1.0.8

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 CHANGED
@@ -115,7 +115,7 @@ await initialize({
115
115
  onPrimary: '#FFFFFF', // Text color on primary background
116
116
  },
117
117
  logo: {
118
- image: Image.resolveAssetSource(require('./assets/logo.png')), // Your custom logo
118
+ image: Image.resolveAssetSource(require('./assets/images/logo.png')), // Your custom logo
119
119
  },
120
120
  }
121
121
  });
@@ -186,7 +186,7 @@ await initialize({
186
186
  onPrimary: '#FFFFFF', // Text color on primary background
187
187
  },
188
188
  logo: {
189
- image: Image.resolveAssetSource(require('./assets/logo.png')),
189
+ image: Image.resolveAssetSource(require('./assets/images/logo.png')),
190
190
  },
191
191
  }
192
192
  });
@@ -351,7 +351,7 @@ await initialize({
351
351
  },
352
352
  // Custom logo
353
353
  logo: {
354
- image: Image.resolveAssetSource(require('./assets/logo.png')),
354
+ image: Image.resolveAssetSource(require('./assets/images/logo.png')),
355
355
  },
356
356
  }
357
357
  });
@@ -408,7 +408,7 @@ const switchToGreenTheme = async () => {
408
408
  - `caption1`: Small captions (default: 12pt)
409
409
  - `caption2`: Extra small captions (default: 10pt)
410
410
 
411
-
411
+
412
412
 
413
413
  **Supported Formats:**
414
414
  - **Colors**: 3-digit (`#F63`), 6-digit (`#FF6633`), 8-digit (`#FF6633FF`) hex codes
@@ -12,7 +12,7 @@ class OctopusReactNativeSdkModule(reactContext: ReactApplicationContext) :
12
12
  private val sdkInitializer = OctopusSDKInitializer()
13
13
  private val eventEmitter = OctopusEventEmitter(reactContext)
14
14
  private val uiController = OctopusUIController(reactContext)
15
- private val ssoAuthenticator = OctopusSSOAuthenticator(eventEmitter)
15
+ private val ssoAuthenticator = OctopusSSOAuthenticator(reactContext, eventEmitter)
16
16
 
17
17
  override fun getName(): String = NAME
18
18
 
@@ -85,10 +85,10 @@ class OctopusReactNativeSdkModule(reactContext: ReactApplicationContext) :
85
85
  promise.reject("UPDATE_THEME_ERROR", "Failed to update theme", e)
86
86
  }
87
87
  }
88
-
88
+
89
89
  private fun parseColor(colorString: String?): String? {
90
90
  if (colorString == null) return null
91
-
91
+
92
92
  return try {
93
93
  // Validate that the color string is a valid hex color
94
94
  android.graphics.Color.parseColor(colorString)
@@ -99,31 +99,31 @@ class OctopusReactNativeSdkModule(reactContext: ReactApplicationContext) :
99
99
  null
100
100
  }
101
101
  }
102
-
102
+
103
103
  private fun parseFontsConfig(fontsMap: ReadableMap): OctopusFontsConfig? {
104
104
  // Use pre-processed configuration from TypeScript layer
105
105
  val parsedConfig = fontsMap.getMap("parsedConfig")
106
106
  if (parsedConfig != null) {
107
107
  return parsePreProcessedFontsConfig(parsedConfig)
108
108
  }
109
-
109
+
110
110
  return null
111
111
  }
112
-
112
+
113
113
  private fun parsePreProcessedFontsConfig(parsedConfig: ReadableMap): OctopusFontsConfig? {
114
114
  val textStylesMap = parsedConfig.getMap("textStyles")
115
115
  val textStyles = mutableMapOf<String, OctopusTextStyleConfig>()
116
-
116
+
117
117
  // Parse pre-processed font configuration from TypeScript layer
118
118
  textStylesMap?.let { textStylesMap ->
119
119
  val textStyleKeys = arrayOf("title1", "title2", "body1", "body2", "caption1", "caption2")
120
-
120
+
121
121
  textStyleKeys.forEach { key ->
122
122
  val textStyleMap = textStylesMap.getMap(key)
123
123
  textStyleMap?.let { style ->
124
124
  val fontType = style.getString("fontType")
125
125
  val fontSize = if (style.hasKey("fontSize")) style.getDouble("fontSize") else Double.NaN
126
-
126
+
127
127
  if (fontType != null || (!fontSize.isNaN() && fontSize > 0)) {
128
128
  textStyles[key] = OctopusTextStyleConfig(
129
129
  fontType = fontType,
@@ -133,14 +133,14 @@ class OctopusReactNativeSdkModule(reactContext: ReactApplicationContext) :
133
133
  }
134
134
  }
135
135
  }
136
-
136
+
137
137
  // Only create fonts config if we have text styles
138
138
  if (textStyles.isNotEmpty()) {
139
139
  return OctopusFontsConfig(
140
140
  textStyles = textStyles
141
141
  )
142
142
  }
143
-
143
+
144
144
  return null
145
145
  }
146
146
 
@@ -18,11 +18,15 @@ class OctopusSDKInitializer {
18
18
 
19
19
  try {
20
20
  val connectionMode = parseConnectionMode(options)
21
-
21
+
22
22
  // Store theme configuration for later use in UI
23
23
  val themeConfig = parseThemeConfig(options)
24
24
  OctopusThemeManager.setThemeConfig(themeConfig)
25
-
25
+
26
+ // Store UI configuration separately
27
+ val uiConfiguration = parseUIConfiguration(options)
28
+ OctopusUIConfigurationManager.setUIConfiguration(uiConfiguration)
29
+
26
30
  OctopusSDK.initialize(
27
31
  context = context,
28
32
  apiKey = apiKey,
@@ -35,19 +39,19 @@ class OctopusSDKInitializer {
35
39
  promise.reject("INITIALIZE_ERROR", "Failed to initialize Octopus SDK", e)
36
40
  }
37
41
  }
38
-
42
+
39
43
  fun parseThemeConfig(options: ReadableMap): OctopusThemeConfig? {
40
-
44
+
41
45
  val themeMap = options.getMap("theme")
42
46
  val colorScheme = options.getString("colorScheme") // Get colorScheme from React Native
43
-
47
+
44
48
  var primaryColor: String? = null
45
49
  var primaryLowContrastColor: String? = null
46
50
  var primaryHighContrastColor: String? = null
47
51
  var onPrimaryColor: String? = null
48
52
  var logoSource: ReadableMap? = null
49
53
  var fontsConfig: OctopusFontsConfig? = null
50
-
54
+
51
55
  // Parse colors from theme if available
52
56
  themeMap?.let { theme ->
53
57
  val colorsMap = theme.getMap("colors")
@@ -55,7 +59,7 @@ class OctopusSDKInitializer {
55
59
  // Check if this is a dual-mode theme (has light and dark properties)
56
60
  val lightColors = colors.getMap("light")
57
61
  val darkColors = colors.getMap("dark")
58
-
62
+
59
63
  if (lightColors != null && darkColors != null) {
60
64
  // Dual-mode theme - select colors based on current color scheme
61
65
  val selectedColors = if (colorScheme == "dark") darkColors else lightColors
@@ -71,7 +75,7 @@ class OctopusSDKInitializer {
71
75
  onPrimaryColor = parseColor(if (colors.hasKey("onPrimary")) colors.getString("onPrimary") else null)
72
76
  }
73
77
  }
74
-
78
+
75
79
  // Handle logo from theme
76
80
  val logoMap = theme.getMap("logo")
77
81
  logoMap?.let { logo ->
@@ -80,7 +84,7 @@ class OctopusSDKInitializer {
80
84
  logoSource = imageSource
81
85
  }
82
86
  }
83
-
87
+
84
88
  // Handle fonts from theme - use pre-processed configuration from TypeScript layer
85
89
  val fontsMap = theme.getMap("fonts")
86
90
  fontsMap?.let { fonts ->
@@ -90,7 +94,7 @@ class OctopusSDKInitializer {
90
94
  }
91
95
  }
92
96
  }
93
-
97
+
94
98
  // Handle logo at root level (for backward compatibility)
95
99
  if (logoSource == null) {
96
100
  val rootLogoMap = options.getMap("logo")
@@ -101,7 +105,7 @@ class OctopusSDKInitializer {
101
105
  }
102
106
  }
103
107
  }
104
-
108
+
105
109
  // Only create theme config if we have at least one customization
106
110
  if (primaryColor != null || logoSource != null || colorScheme != null || fontsConfig != null) {
107
111
  return OctopusThemeConfig(
@@ -114,24 +118,51 @@ class OctopusSDKInitializer {
114
118
  fonts = fontsConfig
115
119
  )
116
120
  }
117
-
121
+
118
122
  return null
119
123
  }
120
-
124
+
125
+ fun parseUIConfiguration(options: ReadableMap): OctopusUIConfiguration? {
126
+ val uiMap = options.getMap("ui")
127
+ var bottomContentPadding: Double? = null
128
+
129
+ uiMap?.let { uiOptions ->
130
+ val candidateKeys = listOf("bottomSafeAreaInset", "bottomPadding", "contentPadding", "contentPaddingBottom")
131
+ for (key in candidateKeys) {
132
+ if (uiOptions.hasKey(key)) {
133
+ bottomContentPadding = try {
134
+ uiOptions.getDouble(key).coerceAtLeast(0.0)
135
+ } catch (e: Exception) {
136
+ null
137
+ }
138
+ if (bottomContentPadding != null) {
139
+ break
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ return if (bottomContentPadding != null) {
146
+ OctopusUIConfiguration(bottomContentPadding = bottomContentPadding)
147
+ } else {
148
+ null
149
+ }
150
+ }
151
+
121
152
  private fun parsePreProcessedFontsConfig(parsedConfig: ReadableMap): OctopusFontsConfig? {
122
153
  val textStylesMap = parsedConfig.getMap("textStyles")
123
154
  val textStyles = mutableMapOf<String, OctopusTextStyleConfig>()
124
-
155
+
125
156
  // Parse pre-processed font configuration from TypeScript layer
126
157
  textStylesMap?.let { textStylesMap ->
127
158
  val textStyleKeys = arrayOf("title1", "title2", "body1", "body2", "caption1", "caption2")
128
-
159
+
129
160
  textStyleKeys.forEach { key ->
130
161
  val textStyleMap = textStylesMap.getMap(key)
131
162
  textStyleMap?.let { style ->
132
163
  val fontType = style.getString("fontType")
133
164
  val fontSize = if (style.hasKey("fontSize")) style.getDouble("fontSize") else Double.NaN
134
-
165
+
135
166
  if (fontType != null || (!fontSize.isNaN() && fontSize > 0)) {
136
167
  textStyles[key] = OctopusTextStyleConfig(
137
168
  fontType = fontType,
@@ -141,18 +172,18 @@ class OctopusSDKInitializer {
141
172
  }
142
173
  }
143
174
  }
144
-
175
+
145
176
  // Only create fonts config if we have text styles
146
177
  if (textStyles.isNotEmpty()) {
147
178
  return OctopusFontsConfig(
148
179
  textStyles = textStyles
149
180
  )
150
181
  }
151
-
182
+
152
183
  return null
153
184
  }
154
185
 
155
-
186
+
156
187
  private fun extractResourceNameFromUri(uri: String): String? {
157
188
  // Extract resource name from React Native image URI
158
189
  // Examples: "logo.png" -> "logo", "images/logo.png" -> "logo"
@@ -163,10 +194,10 @@ class OctopusSDKInitializer {
163
194
  null
164
195
  }
165
196
  }
166
-
197
+
167
198
  private fun parseColor(colorString: String?): String? {
168
199
  if (colorString == null) return null
169
-
200
+
170
201
  return try {
171
202
  // Validate that the color string is a valid hex color
172
203
  Color.parseColor(colorString)
@@ -15,8 +15,13 @@ import java.util.UUID
15
15
  import java.util.concurrent.ConcurrentHashMap
16
16
  import kotlin.coroutines.resume
17
17
  import kotlin.coroutines.resumeWithException
18
+ import android.content.Context
18
19
 
19
- class OctopusSSOAuthenticator(private val eventEmitter: OctopusEventEmitter) {
20
+
21
+ class OctopusSSOAuthenticator(
22
+ private val context: Context,
23
+ private val eventEmitter: OctopusEventEmitter
24
+ ) {
20
25
 
21
26
  private val coroutineScope = CoroutineScope(Dispatchers.Main)
22
27
  private val pendingTokenRequests =
@@ -107,10 +112,22 @@ class OctopusSSOAuthenticator(private val eventEmitter: OctopusEventEmitter) {
107
112
  } else null
108
113
 
109
114
  val profilePicture = profileParams.getString("profilePicture")?.let { pictureUrl ->
110
- if (pictureUrl.isNotBlank() && (pictureUrl.startsWith("http://") || pictureUrl.startsWith("https://"))) {
115
+ if (pictureUrl.startsWith("http://") || pictureUrl.startsWith("https://")) {
111
116
  Image.Remote(pictureUrl)
112
- } else if (pictureUrl.isNotBlank()) {
117
+ } else if (pictureUrl.startsWith("file") || pictureUrl.startsWith("android.resource")) {
113
118
  Image.Local(pictureUrl)
119
+ } else if(pictureUrl.isNotBlank()) {
120
+ // Asset names like "assets_images_logo" need conversion
121
+ // Convert to drawable resource URI
122
+ val resourceId = context.resources.getIdentifier(pictureUrl, "drawable", context.packageName)
123
+ Image.Local(
124
+ if (resourceId != 0) {
125
+ "android.resource://${context.packageName}/$resourceId"
126
+ } else {
127
+ // Fallback: try assets folder
128
+ "file:///android_asset/$pictureUrl"
129
+ }
130
+ )
114
131
  } else {
115
132
  null
116
133
  }
@@ -12,7 +12,9 @@ import android.util.Log
12
12
  import androidx.activity.ComponentActivity
13
13
  import androidx.activity.compose.setContent
14
14
  import androidx.compose.foundation.layout.Box
15
+ import androidx.compose.foundation.layout.PaddingValues
15
16
  import androidx.compose.foundation.layout.fillMaxSize
17
+ import androidx.compose.foundation.layout.padding
16
18
  import androidx.compose.runtime.Composable
17
19
  import androidx.compose.runtime.LaunchedEffect
18
20
  import androidx.compose.runtime.getValue
@@ -20,6 +22,7 @@ import androidx.compose.runtime.mutableStateOf
20
22
  import androidx.compose.runtime.remember
21
23
  import androidx.compose.runtime.setValue
22
24
  import androidx.compose.ui.Modifier
25
+ import androidx.compose.ui.unit.Dp
23
26
  import androidx.compose.ui.graphics.Color
24
27
  import androidx.compose.ui.graphics.asImageBitmap
25
28
  import androidx.compose.ui.graphics.painter.BitmapPainter
@@ -32,11 +35,13 @@ import com.octopuscommunity.sdk.ui.OctopusDrawablesDefaults
32
35
  import com.octopuscommunity.sdk.ui.OctopusTheme
33
36
  import com.octopuscommunity.sdk.ui.OctopusTypographyDefaults
34
37
  import com.octopuscommunity.sdk.ui.OctopusTypography
38
+ import com.octopuscommunity.sdk.ui.home.OctopusHomeDefaults
35
39
  import com.octopuscommunity.sdk.ui.components.OctopusNavigationHandler
36
40
  import androidx.compose.ui.text.TextStyle
37
41
  import androidx.compose.ui.text.font.FontFamily
38
42
  import androidx.compose.ui.text.font.FontWeight
39
43
  import androidx.compose.ui.unit.sp
44
+ import androidx.compose.ui.unit.dp
40
45
  import com.octopuscommunity.sdk.ui.home.OctopusHomeScreen
41
46
  import com.octopuscommunity.sdk.ui.octopusComposables
42
47
  import com.octopuscommunity.sdk.ui.octopusDarkColorScheme
@@ -151,14 +156,18 @@ private fun OctopusUI(onBack: () -> Unit) {
151
156
 
152
157
  // Create typography based on theme config
153
158
  val typography = createCustomTypography(themeConfig)
159
+ val uiConfiguration = OctopusUIConfigurationManager.getUIConfiguration()
154
160
 
155
- // Apply theme with custom colors, logo, and typography
161
+ // Apply theme with custom colors, logo, typography, and content padding
156
162
  OctopusTheme(
157
163
  colorScheme = finalColorScheme,
158
164
  drawables = drawables,
159
165
  typography = typography
160
166
  ) {
161
- OctopusUIContent(onBack = onBack)
167
+ OctopusUIContent(
168
+ bottomContentPadding = uiConfiguration?.bottomContentPadding?.toFloat()?.dp,
169
+ onBack = onBack
170
+ )
162
171
  }
163
172
  }
164
173
 
@@ -213,7 +222,7 @@ private fun createTextStyle(textStyleConfig: OctopusTextStyleConfig?, defaultSty
213
222
 
214
223
 
215
224
  @Composable
216
- private fun OctopusUIContent(onBack: () -> Unit) {
225
+ private fun OctopusUIContent(bottomContentPadding: Dp?, onBack: () -> Unit) {
217
226
  Box(modifier = Modifier.fillMaxSize()) {
218
227
  val navController = rememberNavController()
219
228
 
@@ -222,23 +231,26 @@ private fun OctopusUIContent(onBack: () -> Unit) {
222
231
  startDestination = "OctopusHome"
223
232
  ) {
224
233
  composable(route = "OctopusHome") {
225
- OctopusNavigationHandler(
226
- navigateToLogin = {
227
- OctopusEventEmitter.instance?.emitLoginRequired()
228
- },
229
- navigateToProfileEdit = { fieldToEdit ->
230
- OctopusEventEmitter.instance?.emitEditUser(fieldToEdit)
231
- },
232
- navigateToClientObject = {
233
- // TODO : Bridge Post react
234
- }
235
- ) {
236
- OctopusHomeScreen(
237
- navController = navController,
238
- backIcon = true,
239
- onBack = onBack
240
- )
234
+ OctopusNavigationHandler(
235
+ navigateToLogin = {
236
+ OctopusEventEmitter.instance?.emitLoginRequired()
237
+ },
238
+ navigateToProfileEdit = { fieldToEdit ->
239
+ OctopusEventEmitter.instance?.emitEditUser(fieldToEdit)
240
+ },
241
+ navigateToClientObject = {
242
+ // TODO : Bridge Post react
241
243
  }
244
+ ) {
245
+ OctopusHomeScreen(
246
+ navController = navController,
247
+ backIcon = true,
248
+ floatingActionsPadding = OctopusHomeDefaults.floatingActionsPadding(
249
+ bottom = bottomContentPadding ?: 16.dp
250
+ ),
251
+ onBack = onBack
252
+ )
253
+ }
242
254
  }
243
255
  octopusComposables(
244
256
  navController = navController,
@@ -0,0 +1,6 @@
1
+ package com.octopuscommunity.octopusreactnativesdk
2
+
3
+ data class OctopusUIConfiguration(
4
+ val bottomContentPadding: Double?
5
+ )
6
+
@@ -0,0 +1,12 @@
1
+ package com.octopuscommunity.octopusreactnativesdk
2
+
3
+ object OctopusUIConfigurationManager {
4
+ private var uiConfiguration: OctopusUIConfiguration? = null
5
+
6
+ fun setUIConfiguration(config: OctopusUIConfiguration?) {
7
+ uiConfiguration = config
8
+ }
9
+
10
+ fun getUIConfiguration(): OctopusUIConfiguration? = uiConfiguration
11
+ }
12
+
@@ -17,6 +17,7 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
17
17
  private var theme: OctopusTheme?
18
18
  private var logoSource: [String: Any]?
19
19
  private var fontConfiguration: [String: Any]?
20
+ private var uiConfiguration: OctopusUIConfiguration?
20
21
 
21
22
  // MARK: - RCTBridgeModule
22
23
 
@@ -40,6 +41,7 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
40
41
  self.theme = sdkInitializer.parseTheme(from: options)
41
42
  self.logoSource = sdkInitializer.getLogoSource(from: options)
42
43
  self.fontConfiguration = sdkInitializer.getFontConfiguration(from: options)
44
+ self.uiConfiguration = sdkInitializer.parseUIConfiguration(from: options)
43
45
  resolve(nil)
44
46
  } catch {
45
47
  reject("INITIALIZE_ERROR", "Failed to initialize Octopus SDK: \(error.localizedDescription)", error)
@@ -115,7 +117,13 @@ class OctopusReactNativeSdk: NSObject, RCTBridgeModule {
115
117
 
116
118
  DispatchQueue.main.async {
117
119
  do {
118
- try self.uiManager.openUI(octopus: octopus, theme: self.theme, logoSource: self.logoSource, fontConfiguration: self.fontConfiguration)
120
+ try self.uiManager.openUI(
121
+ octopus: octopus,
122
+ theme: self.theme,
123
+ logoSource: self.logoSource,
124
+ fontConfiguration: self.fontConfiguration,
125
+ uiConfiguration: self.uiConfiguration
126
+ )
119
127
  resolve(nil)
120
128
  } catch {
121
129
  reject("OPEN_UI_ERROR", error.localizedDescription, error)
@@ -1,6 +1,7 @@
1
1
  import Octopus
2
2
  import OctopusUI
3
3
  import SwiftUI
4
+ import CoreGraphics
4
5
 
5
6
  class OctopusSDKInitializer {
6
7
  func initialize(options: [String: Any], eventManager: OctopusEventManager) throws -> OctopusSDK {
@@ -144,6 +145,37 @@ class OctopusSDKInitializer {
144
145
  return nil
145
146
  }
146
147
 
148
+ func parseUIConfiguration(from options: [String: Any]) -> OctopusUIConfiguration? {
149
+ guard let uiOptions = options["ui"] as? [String: Any] else {
150
+ return nil
151
+ }
152
+
153
+ let supportedKeys = [
154
+ "bottomSafeAreaInset",
155
+ "bottomPadding",
156
+ "contentPadding",
157
+ "contentPaddingBottom",
158
+ ]
159
+
160
+ var bottomInset: CGFloat?
161
+
162
+ for key in supportedKeys {
163
+ if let value = uiOptions[key] as? NSNumber {
164
+ bottomInset = CGFloat(truncating: value)
165
+ break
166
+ } else if let value = uiOptions[key] as? Double {
167
+ bottomInset = CGFloat(value)
168
+ break
169
+ }
170
+ }
171
+
172
+ guard let finalInset = bottomInset else {
173
+ return nil
174
+ }
175
+
176
+ return OctopusUIConfiguration(bottomSafeAreaInset: max(0, finalInset))
177
+ }
178
+
147
179
  private func parseConnectionMode(from connectionModeMap: [String: Any], eventManager: OctopusEventManager) throws -> ConnectionMode {
148
180
  let connectionModeType = connectionModeMap["type"] as? String
149
181
 
@@ -0,0 +1,6 @@
1
+ import CoreGraphics
2
+
3
+ struct OctopusUIConfiguration {
4
+ let bottomSafeAreaInset: CGFloat?
5
+ }
6
+
@@ -7,24 +7,35 @@ import React
7
7
  class OctopusUIManager {
8
8
  private weak var presentedViewController: UIViewController?
9
9
 
10
- func openUI(octopus: OctopusSDK, theme: OctopusUI.OctopusTheme?, logoSource: [String: Any]?, fontConfiguration: [String: Any]?) throws {
10
+ func openUI(
11
+ octopus: OctopusSDK,
12
+ theme: OctopusUI.OctopusTheme?,
13
+ logoSource: [String: Any]?,
14
+ fontConfiguration: [String: Any]?,
15
+ uiConfiguration: OctopusUIConfiguration?
16
+ ) throws {
11
17
  guard let presentingViewController = RCTPresentedViewController() else {
12
18
  throw NSError(domain: "OPEN_UI_ERROR", code: 0, userInfo: [NSLocalizedDescriptionKey: "Could not find presenting view controller"])
13
19
  }
14
20
 
15
21
  // Create custom theme with font configuration
16
22
  let customTheme = createCustomTheme(baseTheme: theme, fontConfiguration: fontConfiguration)
17
-
18
- let octopusHomeScreen = OctopusHomeScreen(octopus: octopus)
19
- .environment(\.octopusTheme, customTheme)
20
-
21
- let hostingController = UIHostingController(rootView: AnyView(octopusHomeScreen))
22
- hostingController.modalPresentationStyle = .fullScreen
23
+ let bottomSafeAreaInset = uiConfiguration?.bottomSafeAreaInset
24
+ let initialTheme = (theme != nil || fontConfiguration != nil) ? customTheme : nil
23
25
 
26
+ let hostingController = UIHostingController(
27
+ rootView: makeHomeScreenView(octopus: octopus, theme: initialTheme, bottomSafeAreaInset: bottomSafeAreaInset)
28
+ )
29
+ hostingController.modalPresentationStyle = .fullScreen
30
+
24
31
  // Apply theme if provided
25
32
  if let _ = theme {
26
33
  // Apply theme immediately (without logo) to avoid delay
27
- hostingController.rootView = AnyView(OctopusHomeScreen(octopus: octopus).environment(\.octopusTheme, customTheme))
34
+ hostingController.rootView = makeHomeScreenView(
35
+ octopus: octopus,
36
+ theme: customTheme,
37
+ bottomSafeAreaInset: bottomSafeAreaInset
38
+ )
28
39
 
29
40
  // Then load logo asynchronously and update theme if logo loads
30
41
  if let logoSource = logoSource {
@@ -36,7 +47,11 @@ class OctopusUIManager {
36
47
  fonts: customTheme.fonts,
37
48
  assets: OctopusUI.OctopusTheme.Assets(logo: logoImage)
38
49
  )
39
- hostingController?.rootView = AnyView(OctopusHomeScreen(octopus: octopus).environment(\.octopusTheme, updatedTheme))
50
+ hostingController?.rootView = self.makeHomeScreenView(
51
+ octopus: octopus,
52
+ theme: updatedTheme,
53
+ bottomSafeAreaInset: bottomSafeAreaInset
54
+ )
40
55
  } else {
41
56
  // Theme is already applied, no need to do anything
42
57
  }
@@ -53,7 +68,11 @@ class OctopusUIManager {
53
68
  fonts: OctopusUI.OctopusTheme.Fonts(),
54
69
  assets: OctopusUI.OctopusTheme.Assets(logo: logoImage)
55
70
  )
56
- hostingController?.rootView = AnyView(OctopusHomeScreen(octopus: octopus).environment(\.octopusTheme, logoTheme))
71
+ hostingController?.rootView = self.makeHomeScreenView(
72
+ octopus: octopus,
73
+ theme: logoTheme,
74
+ bottomSafeAreaInset: bottomSafeAreaInset
75
+ )
57
76
  }
58
77
  }
59
78
  }
@@ -142,6 +161,28 @@ class OctopusUIManager {
142
161
  presentedViewController = nil
143
162
  }
144
163
  }
164
+
165
+ private func makeHomeScreenView(octopus: OctopusSDK, theme: OctopusUI.OctopusTheme?, bottomSafeAreaInset: CGFloat?) -> AnyView {
166
+ if let inset = bottomSafeAreaInset {
167
+ if let theme = theme {
168
+ return AnyView(
169
+ OctopusHomeScreen(octopus: octopus, bottomSafeAreaInset: inset)
170
+ .environment(\.octopusTheme, theme)
171
+ )
172
+ }
173
+
174
+ return AnyView(OctopusHomeScreen(octopus: octopus, bottomSafeAreaInset: inset))
175
+ }
176
+
177
+ if let theme = theme {
178
+ return AnyView(
179
+ OctopusHomeScreen(octopus: octopus)
180
+ .environment(\.octopusTheme, theme)
181
+ )
182
+ }
183
+
184
+ return AnyView(OctopusHomeScreen(octopus: octopus))
185
+ }
145
186
 
146
187
  private func createCustomTheme(baseTheme: OctopusUI.OctopusTheme?, fontConfiguration: [String: Any]?) -> OctopusUI.OctopusTheme {
147
188
  // If no font configuration, return the base theme or default
@@ -33,6 +33,10 @@ import { parseFontConfig } from "./internals/fontParser.js";
33
33
  * 2. Dual mode colors - separate color sets for light and dark modes
34
34
  */
35
35
 
36
+ /**
37
+ * UI customization options for platform-specific layout adjustments.
38
+ */
39
+
36
40
  /**
37
41
  * Configuration params for initializing the Octopus SDK.
38
42
  */
@@ -1 +1 @@
1
- {"version":3,"names":["OctopusReactNativeSdk","Appearance","colorSchemeManager","parseFontConfig","initialize","params","colorScheme","getColorScheme","processedTheme","theme","fonts","parsedConfig","undefined","paramsWithColorScheme","setTheme","startListening"],"sourceRoot":"../../src","sources":["initialize.ts"],"mappings":";;AAAA,SAASA,qBAAqB,QAAQ,6BAA0B;AAGhE,SAASC,UAAU,QAAQ,cAAc;AACzC,SAASC,kBAAkB,QAAQ,mCAAgC;AACnE,SAASC,eAAe,QAA+B,2BAAwB;;AAE/E;AACA;AACA;;AAYA;AACA;AACA;;AAGA;AACA;AACA;;AAMA;AACA;AACA;;AAQA;AACA;AACA;;AA0BA;AACA;AACA;AACA;AACA;AACA;AACA;;AAyBA;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;EACA,MAAMC,WAAW,GAAGL,UAAU,CAACM,cAAc,CAAC,CAAC;;EAE/C;EACA,MAAMC,cAAc,GAAGH,MAAM,CAACI,KAAK,GAC/B;IACE,GAAGJ,MAAM,CAACI,KAAK;IACfC,KAAK,EAAEL,MAAM,CAACI,KAAK,CAACC,KAAK,GACrB;MACE,GAAGL,MAAM,CAACI,KAAK,CAACC,KAAK;MACrBC,YAAY,EAAER,eAAe,CAACE,MAAM,CAACI,KAAK,CAACC,KAAK;IAClD,CAAC,GACDE;EACN,CAAC,GACDA,SAAS;EAEb,MAAMC,qBAAqB,GAAG;IAC5B,GAAGR,MAAM;IACTI,KAAK,EAAED,cAAc;IACrBF,WAAW,EAAEA,WAAW,IAAIM;EAC9B,CAAC;;EAED;EACAV,kBAAkB,CAACY,QAAQ,CAACN,cAAc,IAAI,IAAI,CAAC;;EAEnD;EACAN,kBAAkB,CAACa,cAAc,CAAC,CAAC;EAEnC,OAAOf,qBAAqB,CAACI,UAAU,CAACS,qBAAqB,CAAC;AAChE","ignoreList":[]}
1
+ {"version":3,"names":["OctopusReactNativeSdk","Appearance","colorSchemeManager","parseFontConfig","initialize","params","colorScheme","getColorScheme","processedTheme","theme","fonts","parsedConfig","undefined","paramsWithColorScheme","setTheme","startListening"],"sourceRoot":"../../src","sources":["initialize.ts"],"mappings":";;AAAA,SAASA,qBAAqB,QAAQ,6BAA0B;AAGhE,SAASC,UAAU,QAAQ,cAAc;AACzC,SAASC,kBAAkB,QAAQ,mCAAgC;AACnE,SAASC,eAAe,QAA+B,2BAAwB;;AAE/E;AACA;AACA;;AAYA;AACA;AACA;;AAGA;AACA;AACA;;AAMA;AACA;AACA;;AAQA;AACA;AACA;;AA0BA;AACA;AACA;AACA;AACA;AACA;AACA;;AAyBA;AACA;AACA;;AAUA;AACA;AACA;;AA0BA;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;EACA,MAAMC,WAAW,GAAGL,UAAU,CAACM,cAAc,CAAC,CAAC;;EAE/C;EACA,MAAMC,cAAc,GAAGH,MAAM,CAACI,KAAK,GAC/B;IACE,GAAGJ,MAAM,CAACI,KAAK;IACfC,KAAK,EAAEL,MAAM,CAACI,KAAK,CAACC,KAAK,GACrB;MACE,GAAGL,MAAM,CAACI,KAAK,CAACC,KAAK;MACrBC,YAAY,EAAER,eAAe,CAACE,MAAM,CAACI,KAAK,CAACC,KAAK;IAClD,CAAC,GACDE;EACN,CAAC,GACDA,SAAS;EAEb,MAAMC,qBAAqB,GAAG;IAC5B,GAAGR,MAAM;IACTI,KAAK,EAAED,cAAc;IACrBF,WAAW,EAAEA,WAAW,IAAIM;EAC9B,CAAC;;EAED;EACAV,kBAAkB,CAACY,QAAQ,CAACN,cAAc,IAAI,IAAI,CAAC;;EAEnD;EACAN,kBAAkB,CAACa,cAAc,CAAC,CAAC;EAEnC,OAAOf,qBAAqB,CAACI,UAAU,CAACS,qBAAqB,CAAC;AAChE","ignoreList":[]}
@@ -89,6 +89,17 @@ export interface OctopusTheme {
89
89
  image?: ImageResolvedAssetSource;
90
90
  };
91
91
  }
92
+ /**
93
+ * UI customization options for platform-specific layout adjustments.
94
+ */
95
+ export interface OctopusUIOptions {
96
+ /**
97
+ * Additional bottom inset applied to the Octopus UI.
98
+ * - On iOS, mapped to `bottomSafeAreaInset` (points)
99
+ * - On Android, mapped to `contentPadding` bottom (dp)
100
+ */
101
+ bottomSafeAreaInset?: number;
102
+ }
92
103
  /**
93
104
  * Configuration params for initializing the Octopus SDK.
94
105
  */
@@ -111,6 +122,8 @@ export interface InitializeParams {
111
122
  };
112
123
  /** Optional theme customization for the Octopus UI */
113
124
  theme?: OctopusTheme;
125
+ /** Optional UI customization for layout-related tweaks */
126
+ ui?: OctopusUIOptions;
114
127
  }
115
128
  /**
116
129
  * 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;AACjE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAG7D,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gGAAgG;IAChG,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,UAAU,CAAC,EAAE;QACX,oCAAoC;QACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,oCAAoC;QACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,mCAAmC;QACnC,KAAK,CAAC,EAAE,gBAAgB,CAAC;QACzB,mCAAmC;QACnC,KAAK,CAAC,EAAE,gBAAgB,CAAC;QACzB,sCAAsC;QACtC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;QAC5B,sCAAsC;QACtC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;KAC7B,CAAC;IACF;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACxC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,MAAM,CAAC,EACH,eAAe,GACf;QACE,4BAA4B;QAC5B,KAAK,EAAE,eAAe,CAAC;QACvB,2BAA2B;QAC3B,IAAI,EAAE,eAAe,CAAC;KACvB,CAAC;IACN,iCAAiC;IACjC,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,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,CA8BlE"}
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;AAG7D,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gGAAgG;IAChG,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,UAAU,CAAC,EAAE;QACX,oCAAoC;QACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,oCAAoC;QACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QAC1B,mCAAmC;QACnC,KAAK,CAAC,EAAE,gBAAgB,CAAC;QACzB,mCAAmC;QACnC,KAAK,CAAC,EAAE,gBAAgB,CAAC;QACzB,sCAAsC;QACtC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;QAC5B,sCAAsC;QACtC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;KAC7B,CAAC;IACF;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACxC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,MAAM,CAAC,EACH,eAAe,GACf;QACE,4BAA4B;QAC5B,KAAK,EAAE,eAAe,CAAC;QACvB,2BAA2B;QAC3B,IAAI,EAAE,eAAe,CAAC;KACvB,CAAC;IACN,iCAAiC;IACjC,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,yBAAyB;IACzB,IAAI,CAAC,EAAE;QACL,0FAA0F;QAC1F,KAAK,CAAC,EAAE,wBAAwB,CAAC;KAClC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;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;IACrB,0DAA0D;IAC1D,EAAE,CAAC,EAAE,gBAAgB,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BlE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octopus-community/react-native",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "React Native module for the Octopus Community SDK",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",
package/src/initialize.ts CHANGED
@@ -101,6 +101,18 @@ export interface OctopusTheme {
101
101
  };
102
102
  }
103
103
 
104
+ /**
105
+ * UI customization options for platform-specific layout adjustments.
106
+ */
107
+ export interface OctopusUIOptions {
108
+ /**
109
+ * Additional bottom inset applied to the Octopus UI.
110
+ * - On iOS, mapped to `bottomSafeAreaInset` (points)
111
+ * - On Android, mapped to `contentPadding` bottom (dp)
112
+ */
113
+ bottomSafeAreaInset?: number;
114
+ }
115
+
104
116
  /**
105
117
  * Configuration params for initializing the Octopus SDK.
106
118
  */
@@ -125,6 +137,8 @@ export interface InitializeParams {
125
137
  };
126
138
  /** Optional theme customization for the Octopus UI */
127
139
  theme?: OctopusTheme;
140
+ /** Optional UI customization for layout-related tweaks */
141
+ ui?: OctopusUIOptions;
128
142
  }
129
143
 
130
144
  /**