@octopus-community/react-native 1.0.2 → 1.0.4
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 +257 -12
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactNativeSdkModule.kt +82 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt +93 -17
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeConfig.kt +12 -1
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt +135 -56
- package/ios/OctopusReactNativeSdk.mm +8 -0
- package/ios/OctopusReactNativeSdk.swift +21 -1
- package/ios/OctopusSDKInitializer.swift +95 -24
- package/ios/OctopusUIManager.swift +106 -11
- package/lib/module/initialize.js +50 -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 +78 -10
- 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 +117 -12
- package/src/internals/colorSchemeManager.ts +113 -0
- package/src/internals/fontParser.ts +64 -0
package/README.md
CHANGED
|
@@ -168,7 +168,9 @@ Future versions of this React Native module will let you show the UI in your Rea
|
|
|
168
168
|
|
|
169
169
|
### Theme Customization
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
The Octopus SDK provides comprehensive theming capabilities to match your app's branding. You can customize colors, fonts, and logo, with full support for both light and dark modes.
|
|
172
|
+
|
|
173
|
+
#### Basic Theme Setup
|
|
172
174
|
|
|
173
175
|
```ts
|
|
174
176
|
import { Image } from 'react-native';
|
|
@@ -184,33 +186,276 @@ await initialize({
|
|
|
184
186
|
onPrimary: '#FFFFFF', // Text color on primary background
|
|
185
187
|
},
|
|
186
188
|
logo: {
|
|
187
|
-
// Use Image.resolveAssetSource() for bundled image assets
|
|
188
189
|
image: Image.resolveAssetSource(require('./assets/logo.png')),
|
|
189
190
|
},
|
|
190
191
|
}
|
|
191
192
|
});
|
|
192
193
|
```
|
|
193
194
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
195
|
+
#### Dark/Light Mode Management
|
|
196
|
+
|
|
197
|
+
The SDK automatically handles system appearance changes, but you can also force specific modes:
|
|
198
|
+
|
|
199
|
+
##### System Mode (Default)
|
|
200
|
+
```ts
|
|
201
|
+
// The SDK automatically follows the system appearance
|
|
202
|
+
// No additional configuration needed
|
|
203
|
+
await initialize({
|
|
204
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
205
|
+
connectionMode: { type: 'octopus' },
|
|
206
|
+
theme: { /* your theme */ }
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
##### Forced Light Mode
|
|
211
|
+
```ts
|
|
212
|
+
import { Appearance } from 'react-native';
|
|
213
|
+
|
|
214
|
+
// Force light mode for your entire app
|
|
215
|
+
Appearance.setColorScheme('light');
|
|
216
|
+
|
|
217
|
+
await initialize({
|
|
218
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
219
|
+
connectionMode: { type: 'octopus' },
|
|
220
|
+
theme: { /* your theme */ }
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
##### Forced Dark Mode
|
|
225
|
+
```ts
|
|
226
|
+
import { Appearance } from 'react-native';
|
|
227
|
+
|
|
228
|
+
// Force dark mode for your entire app
|
|
229
|
+
Appearance.setColorScheme('dark');
|
|
230
|
+
|
|
231
|
+
await initialize({
|
|
232
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
233
|
+
connectionMode: { type: 'octopus' },
|
|
234
|
+
theme: { /* your theme */ }
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Dual-Mode Color Themes
|
|
239
|
+
|
|
240
|
+
For enhanced theming, you can provide separate color sets for light and dark modes:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
await initialize({
|
|
244
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
245
|
+
connectionMode: { type: 'octopus' },
|
|
246
|
+
theme: {
|
|
247
|
+
colors: {
|
|
248
|
+
light: {
|
|
249
|
+
primary: '#3B82F6', // Blue for light mode
|
|
250
|
+
primaryLowContrast: '#60A5FA',
|
|
251
|
+
primaryHighContrast: '#1D4ED8',
|
|
252
|
+
onPrimary: '#FFFFFF',
|
|
253
|
+
},
|
|
254
|
+
dark: {
|
|
255
|
+
primary: '#60A5FA', // Lighter blue for dark mode
|
|
256
|
+
primaryLowContrast: '#93C5FD',
|
|
257
|
+
primaryHighContrast: '#3B82F6',
|
|
258
|
+
onPrimary: '#000000',
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Font Customization
|
|
266
|
+
|
|
267
|
+
Customize typography with text styles and font sizes:
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
await initialize({
|
|
271
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
272
|
+
connectionMode: { type: 'octopus' },
|
|
273
|
+
theme: {
|
|
274
|
+
fonts: {
|
|
275
|
+
textStyles: {
|
|
276
|
+
title1: {
|
|
277
|
+
fontType: 'serif', // serif, monospace, or default
|
|
278
|
+
fontSize: { size: 28 }
|
|
279
|
+
},
|
|
280
|
+
title2: {
|
|
281
|
+
fontType: 'serif',
|
|
282
|
+
fontSize: { size: 22 }
|
|
283
|
+
},
|
|
284
|
+
body1: {
|
|
285
|
+
fontType: 'default', // Uses system default
|
|
286
|
+
fontSize: { size: 16 }
|
|
287
|
+
},
|
|
288
|
+
body2: {
|
|
289
|
+
fontSize: { size: 14 } // Only size, uses default font type
|
|
290
|
+
},
|
|
291
|
+
caption1: {
|
|
292
|
+
fontType: 'monospace',
|
|
293
|
+
fontSize: { size: 12 }
|
|
294
|
+
},
|
|
295
|
+
caption2: {
|
|
296
|
+
fontSize: { size: 10 }
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### Theme Application
|
|
305
|
+
|
|
306
|
+
Themes are applied when the Octopus UI is opened. The SDK automatically detects the current system appearance (light/dark mode) and applies the appropriate theme configuration.
|
|
307
|
+
|
|
308
|
+
#### Complete Theme Example
|
|
309
|
+
|
|
310
|
+
Here's a comprehensive example showing all theming options:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
import { Image, Appearance } from 'react-native';
|
|
314
|
+
import { initialize } from '@octopus-community/react-native';
|
|
315
|
+
|
|
316
|
+
// Force dark mode (optional)
|
|
317
|
+
Appearance.setColorScheme('dark');
|
|
318
|
+
|
|
319
|
+
await initialize({
|
|
320
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
321
|
+
connectionMode: {
|
|
322
|
+
type: 'sso',
|
|
323
|
+
appManagedFields: ['username', 'profilePicture']
|
|
324
|
+
},
|
|
325
|
+
theme: {
|
|
326
|
+
// Dual-mode colors
|
|
327
|
+
colors: {
|
|
328
|
+
light: {
|
|
329
|
+
primary: '#8B5CF6',
|
|
330
|
+
primaryLowContrast: '#A78BFA',
|
|
331
|
+
primaryHighContrast: '#7C3AED',
|
|
332
|
+
onPrimary: '#FFFFFF',
|
|
333
|
+
},
|
|
334
|
+
dark: {
|
|
335
|
+
primary: '#A78BFA',
|
|
336
|
+
primaryLowContrast: '#C4B5FD',
|
|
337
|
+
primaryHighContrast: '#8B5CF6',
|
|
338
|
+
onPrimary: '#000000',
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
// Custom fonts
|
|
342
|
+
fonts: {
|
|
343
|
+
textStyles: {
|
|
344
|
+
title1: { fontType: 'serif', fontSize: { size: 28 } },
|
|
345
|
+
title2: { fontType: 'serif', fontSize: { size: 22 } },
|
|
346
|
+
body1: { fontSize: { size: 16 } },
|
|
347
|
+
body2: { fontSize: { size: 14 } },
|
|
348
|
+
caption1: { fontType: 'monospace', fontSize: { size: 12 } },
|
|
349
|
+
caption2: { fontSize: { size: 10 } },
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
// Custom logo
|
|
353
|
+
logo: {
|
|
354
|
+
image: Image.resolveAssetSource(require('./assets/logo.png')),
|
|
355
|
+
},
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### Dynamic Theme Switching
|
|
200
361
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
362
|
+
To change themes in your app, you need to re-initialize the SDK with the new theme configuration:
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
// Example: Switch between different theme sets
|
|
366
|
+
const switchToGreenTheme = async () => {
|
|
367
|
+
await initialize({
|
|
368
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
369
|
+
connectionMode: { type: 'sso', appManagedFields: ['username'] },
|
|
370
|
+
theme: {
|
|
371
|
+
colors: {
|
|
372
|
+
light: {
|
|
373
|
+
primary: '#10B981',
|
|
374
|
+
primaryLowContrast: '#34D399',
|
|
375
|
+
primaryHighContrast: '#059669',
|
|
376
|
+
onPrimary: '#FFFFFF',
|
|
377
|
+
},
|
|
378
|
+
dark: {
|
|
379
|
+
primary: '#34D399',
|
|
380
|
+
primaryLowContrast: '#6EE7B7',
|
|
381
|
+
primaryHighContrast: '#10B981',
|
|
382
|
+
onPrimary: '#000000',
|
|
383
|
+
},
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
};
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
#### Theme Configuration Reference
|
|
391
|
+
|
|
392
|
+
**Color Properties:**
|
|
393
|
+
- `primary`: Main brand color (hex format: `#FF6B35` or `FF6B35`)
|
|
394
|
+
- `primaryLowContrast`: Lighter variation for subtle elements
|
|
395
|
+
- `primaryHighContrast`: Darker variation for high contrast needs
|
|
396
|
+
- `onPrimary`: Text color displayed over primary background
|
|
397
|
+
|
|
398
|
+
**Font Types:**
|
|
399
|
+
- `default`: System default font
|
|
400
|
+
- `serif`: Serif font family
|
|
401
|
+
- `monospace`: Monospace font family
|
|
402
|
+
|
|
403
|
+
**Text Styles:**
|
|
404
|
+
- `title1`: Large titles (default: 28pt)
|
|
405
|
+
- `title2`: Medium titles (default: 22pt)
|
|
406
|
+
- `body1`: Primary body text (default: 16pt)
|
|
407
|
+
- `body2`: Secondary body text (default: 14pt)
|
|
408
|
+
- `caption1`: Small captions (default: 12pt)
|
|
409
|
+
- `caption2`: Extra small captions (default: 10pt)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
**Supported Formats:**
|
|
414
|
+
- **Colors**: 3-digit (`#F63`), 6-digit (`#FF6633`), 8-digit (`#FF6633FF`) hex codes
|
|
415
|
+
- **Images**: Use `Image.resolveAssetSource(require('./path/to/image.png'))` for bundled assets
|
|
416
|
+
- **Fonts**: Choose between `serif`, `monospace`, or `default` font types
|
|
417
|
+
|
|
418
|
+
**Platform Behavior:**
|
|
419
|
+
- **iOS**: Uses adaptive colors that automatically respond to system appearance changes
|
|
420
|
+
- **Android**: Theme is applied when the UI opens and reflects the current system appearance
|
|
421
|
+
- **Theme Changes**: Require re-initializing the SDK with new theme configuration
|
|
422
|
+
- **System Mode**: Automatically follows device light/dark mode settings
|
|
423
|
+
- **Forced Mode**: Use `Appearance.setColorScheme('light'|'dark')` to override system settings
|
|
204
424
|
|
|
205
425
|
**Note**: All theme properties are optional. If not provided, the default Octopus theme will be used.
|
|
206
426
|
|
|
427
|
+
For more detailed theming information and advanced customization options, see the [Octopus Community iOS SDK theming documentation](https://doc.octopuscommunity.com/SDK/sso/ios#modify-the-theme).
|
|
428
|
+
|
|
207
429
|
### API docs
|
|
208
430
|
|
|
209
431
|
For details about the Typescript API, head to the [API docs](./docs/api/README.md).
|
|
210
432
|
|
|
433
|
+
#### Key Theming Functions
|
|
434
|
+
|
|
435
|
+
- **`initialize(params)`**: Initialize the SDK with theme configuration
|
|
436
|
+
- **`Appearance.setColorScheme(mode)`**: Force light/dark mode (React Native API)
|
|
437
|
+
- **`openUI()`**: Open the Octopus UI with the current theme applied
|
|
438
|
+
|
|
211
439
|
### Example app
|
|
212
440
|
|
|
213
|
-
Take a look at our [example app](./example/src/App.tsx)
|
|
441
|
+
Take a look at our [example app](./example/src/App.tsx) which demonstrates:
|
|
442
|
+
|
|
443
|
+
- **Complete theming system** with multiple color themes and font customizations
|
|
444
|
+
- **Dark/Light mode management** with system detection and forced modes
|
|
445
|
+
- **Dual-mode color themes** with separate light and dark color sets
|
|
446
|
+
- **Font customization** with different font types (serif, monospace, default) and sizes
|
|
447
|
+
- **Theme switching** by re-initializing the SDK with new configurations
|
|
448
|
+
- **Logo customization** with bundled image assets
|
|
449
|
+
- **Interactive theme controls** to test different configurations
|
|
450
|
+
|
|
451
|
+
The example app includes a comprehensive theme selector interface that lets you:
|
|
452
|
+
- Switch between system, light, and dark modes
|
|
453
|
+
- Choose from multiple pre-defined color themes (Theme 1, 2, 3, or None)
|
|
454
|
+
- Customize font types (default, serif, monospace) and sizes (small, default, large)
|
|
455
|
+
- Toggle custom logo display
|
|
456
|
+
- See theme changes applied when the UI reopens
|
|
457
|
+
|
|
458
|
+
This is the perfect starting point to understand how to implement theming in your own React Native app.
|
|
214
459
|
|
|
215
460
|
## Troubleshooting
|
|
216
461
|
|
|
@@ -63,6 +63,88 @@ class OctopusReactNativeSdkModule(reactContext: ReactApplicationContext) :
|
|
|
63
63
|
eventEmitter.removeListeners(count)
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
@ReactMethod
|
|
67
|
+
fun updateColorScheme(colorScheme: String?, promise: Promise) {
|
|
68
|
+
// Update the theme config with the new color scheme
|
|
69
|
+
val currentThemeConfig = OctopusThemeManager.getThemeConfig()
|
|
70
|
+
if (currentThemeConfig != null) {
|
|
71
|
+
val updatedThemeConfig = currentThemeConfig.copy(colorScheme = colorScheme)
|
|
72
|
+
OctopusThemeManager.setThemeConfig(updatedThemeConfig)
|
|
73
|
+
}
|
|
74
|
+
promise.resolve(null)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@ReactMethod
|
|
78
|
+
fun updateTheme(themeOptions: ReadableMap, promise: Promise) {
|
|
79
|
+
try {
|
|
80
|
+
// Parse theme using the standard initializer
|
|
81
|
+
val themeConfig = sdkInitializer.parseThemeConfig(themeOptions)
|
|
82
|
+
OctopusThemeManager.setThemeConfig(themeConfig)
|
|
83
|
+
promise.resolve(null)
|
|
84
|
+
} catch (e: Exception) {
|
|
85
|
+
promise.reject("UPDATE_THEME_ERROR", "Failed to update theme", e)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private fun parseColor(colorString: String?): String? {
|
|
90
|
+
if (colorString == null) return null
|
|
91
|
+
|
|
92
|
+
return try {
|
|
93
|
+
// Validate that the color string is a valid hex color
|
|
94
|
+
android.graphics.Color.parseColor(colorString)
|
|
95
|
+
// Return the original string if parsing succeeds
|
|
96
|
+
colorString
|
|
97
|
+
} catch (e: IllegalArgumentException) {
|
|
98
|
+
// Invalid color format - return null to skip this color
|
|
99
|
+
null
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private fun parseFontsConfig(fontsMap: ReadableMap): OctopusFontsConfig? {
|
|
104
|
+
// Use pre-processed configuration from TypeScript layer
|
|
105
|
+
val parsedConfig = fontsMap.getMap("parsedConfig")
|
|
106
|
+
if (parsedConfig != null) {
|
|
107
|
+
return parsePreProcessedFontsConfig(parsedConfig)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return null
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private fun parsePreProcessedFontsConfig(parsedConfig: ReadableMap): OctopusFontsConfig? {
|
|
114
|
+
val textStylesMap = parsedConfig.getMap("textStyles")
|
|
115
|
+
val textStyles = mutableMapOf<String, OctopusTextStyleConfig>()
|
|
116
|
+
|
|
117
|
+
// Parse pre-processed font configuration from TypeScript layer
|
|
118
|
+
textStylesMap?.let { textStylesMap ->
|
|
119
|
+
val textStyleKeys = arrayOf("title1", "title2", "body1", "body2", "caption1", "caption2")
|
|
120
|
+
|
|
121
|
+
textStyleKeys.forEach { key ->
|
|
122
|
+
val textStyleMap = textStylesMap.getMap(key)
|
|
123
|
+
textStyleMap?.let { style ->
|
|
124
|
+
val fontType = style.getString("fontType")
|
|
125
|
+
val fontSize = if (style.hasKey("fontSize")) style.getDouble("fontSize") else Double.NaN
|
|
126
|
+
|
|
127
|
+
if (fontType != null || (!fontSize.isNaN() && fontSize > 0)) {
|
|
128
|
+
textStyles[key] = OctopusTextStyleConfig(
|
|
129
|
+
fontType = fontType,
|
|
130
|
+
fontSize = if (fontSize.isNaN() || fontSize <= 0) null else fontSize
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Only create fonts config if we have text styles
|
|
138
|
+
if (textStyles.isNotEmpty()) {
|
|
139
|
+
return OctopusFontsConfig(
|
|
140
|
+
textStyles = textStyles
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
66
148
|
companion object {
|
|
67
149
|
const val NAME = "OctopusReactNativeSdk"
|
|
68
150
|
}
|
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt
CHANGED
|
@@ -36,47 +36,123 @@ class OctopusSDKInitializer {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
val themeMap = options.getMap("theme") ?: return null
|
|
39
|
+
fun parseThemeConfig(options: ReadableMap): OctopusThemeConfig? {
|
|
41
40
|
|
|
42
|
-
val
|
|
43
|
-
val
|
|
41
|
+
val themeMap = options.getMap("theme")
|
|
42
|
+
val colorScheme = options.getString("colorScheme") // Get colorScheme from React Native
|
|
44
43
|
|
|
45
44
|
var primaryColor: String? = null
|
|
46
45
|
var primaryLowContrastColor: String? = null
|
|
47
46
|
var primaryHighContrastColor: String? = null
|
|
48
47
|
var onPrimaryColor: String? = null
|
|
49
48
|
var logoSource: ReadableMap? = null
|
|
49
|
+
var fontsConfig: OctopusFontsConfig? = null
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
// Parse colors from theme if available
|
|
52
|
+
themeMap?.let { theme ->
|
|
53
|
+
val colorsMap = theme.getMap("colors")
|
|
54
|
+
colorsMap?.let { colors ->
|
|
55
|
+
// Check if this is a dual-mode theme (has light and dark properties)
|
|
56
|
+
val lightColors = colors.getMap("light")
|
|
57
|
+
val darkColors = colors.getMap("dark")
|
|
58
|
+
|
|
59
|
+
if (lightColors != null && darkColors != null) {
|
|
60
|
+
// Dual-mode theme - select colors based on current color scheme
|
|
61
|
+
val selectedColors = if (colorScheme == "dark") darkColors else lightColors
|
|
62
|
+
primaryColor = parseColor(if (selectedColors.hasKey("primary")) selectedColors.getString("primary") else null)
|
|
63
|
+
primaryLowContrastColor = parseColor(if (selectedColors.hasKey("primaryLowContrast")) selectedColors.getString("primaryLowContrast") else null)
|
|
64
|
+
primaryHighContrastColor = parseColor(if (selectedColors.hasKey("primaryHighContrast")) selectedColors.getString("primaryHighContrast") else null)
|
|
65
|
+
onPrimaryColor = parseColor(if (selectedColors.hasKey("onPrimary")) selectedColors.getString("onPrimary") else null)
|
|
66
|
+
} else {
|
|
67
|
+
// Single-mode theme (backward compatibility)
|
|
68
|
+
primaryColor = parseColor(if (colors.hasKey("primary")) colors.getString("primary") else null)
|
|
69
|
+
primaryLowContrastColor = parseColor(if (colors.hasKey("primaryLowContrast")) colors.getString("primaryLowContrast") else null)
|
|
70
|
+
primaryHighContrastColor = parseColor(if (colors.hasKey("primaryHighContrast")) colors.getString("primaryHighContrast") else null)
|
|
71
|
+
onPrimaryColor = parseColor(if (colors.hasKey("onPrimary")) colors.getString("onPrimary") else null)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Handle logo from theme
|
|
76
|
+
val logoMap = theme.getMap("logo")
|
|
77
|
+
logoMap?.let { logo ->
|
|
78
|
+
val imageSource = logo.getMap("image")
|
|
79
|
+
if (imageSource != null) {
|
|
80
|
+
logoSource = imageSource
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Handle fonts from theme - use pre-processed configuration from TypeScript layer
|
|
85
|
+
val fontsMap = theme.getMap("fonts")
|
|
86
|
+
fontsMap?.let { fonts ->
|
|
87
|
+
val parsedConfig = fonts.getMap("parsedConfig")
|
|
88
|
+
if (parsedConfig != null) {
|
|
89
|
+
fontsConfig = parsePreProcessedFontsConfig(parsedConfig)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
56
92
|
}
|
|
57
93
|
|
|
58
|
-
// Handle logo
|
|
59
|
-
|
|
60
|
-
val
|
|
61
|
-
|
|
62
|
-
|
|
94
|
+
// Handle logo at root level (for backward compatibility)
|
|
95
|
+
if (logoSource == null) {
|
|
96
|
+
val rootLogoMap = options.getMap("logo")
|
|
97
|
+
rootLogoMap?.let { logo ->
|
|
98
|
+
val imageSource = logo.getMap("image")
|
|
99
|
+
if (imageSource != null) {
|
|
100
|
+
logoSource = imageSource
|
|
101
|
+
}
|
|
63
102
|
}
|
|
64
103
|
}
|
|
65
104
|
|
|
66
105
|
// Only create theme config if we have at least one customization
|
|
67
|
-
if (primaryColor != null || logoSource != null) {
|
|
106
|
+
if (primaryColor != null || logoSource != null || colorScheme != null || fontsConfig != null) {
|
|
68
107
|
return OctopusThemeConfig(
|
|
69
108
|
primaryColor = primaryColor,
|
|
70
109
|
primaryLowContrastColor = primaryLowContrastColor,
|
|
71
110
|
primaryHighContrastColor = primaryHighContrastColor,
|
|
72
111
|
onPrimaryColor = onPrimaryColor,
|
|
73
|
-
logoSource = logoSource
|
|
112
|
+
logoSource = logoSource,
|
|
113
|
+
colorScheme = colorScheme,
|
|
114
|
+
fonts = fontsConfig
|
|
74
115
|
)
|
|
75
116
|
}
|
|
76
117
|
|
|
77
118
|
return null
|
|
78
119
|
}
|
|
79
120
|
|
|
121
|
+
private fun parsePreProcessedFontsConfig(parsedConfig: ReadableMap): OctopusFontsConfig? {
|
|
122
|
+
val textStylesMap = parsedConfig.getMap("textStyles")
|
|
123
|
+
val textStyles = mutableMapOf<String, OctopusTextStyleConfig>()
|
|
124
|
+
|
|
125
|
+
// Parse pre-processed font configuration from TypeScript layer
|
|
126
|
+
textStylesMap?.let { textStylesMap ->
|
|
127
|
+
val textStyleKeys = arrayOf("title1", "title2", "body1", "body2", "caption1", "caption2")
|
|
128
|
+
|
|
129
|
+
textStyleKeys.forEach { key ->
|
|
130
|
+
val textStyleMap = textStylesMap.getMap(key)
|
|
131
|
+
textStyleMap?.let { style ->
|
|
132
|
+
val fontType = style.getString("fontType")
|
|
133
|
+
val fontSize = if (style.hasKey("fontSize")) style.getDouble("fontSize") else Double.NaN
|
|
134
|
+
|
|
135
|
+
if (fontType != null || (!fontSize.isNaN() && fontSize > 0)) {
|
|
136
|
+
textStyles[key] = OctopusTextStyleConfig(
|
|
137
|
+
fontType = fontType,
|
|
138
|
+
fontSize = if (fontSize.isNaN() || fontSize <= 0) null else fontSize
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Only create fonts config if we have text styles
|
|
146
|
+
if (textStyles.isNotEmpty()) {
|
|
147
|
+
return OctopusFontsConfig(
|
|
148
|
+
textStyles = textStyles
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return null
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
80
156
|
private fun extractResourceNameFromUri(uri: String): String? {
|
|
81
157
|
// Extract resource name from React Native image URI
|
|
82
158
|
// Examples: "logo.png" -> "logo", "images/logo.png" -> "logo"
|
|
@@ -108,7 +184,7 @@ class OctopusSDKInitializer {
|
|
|
108
184
|
"sso" -> {
|
|
109
185
|
ConnectionMode.SSO(
|
|
110
186
|
appManagedFields = ProfileFieldMapper.fromReactNativeArray(
|
|
111
|
-
connectionModeMap.getArray("appManagedFields")
|
|
187
|
+
if (connectionModeMap.hasKey("appManagedFields")) connectionModeMap.getArray("appManagedFields") else null
|
|
112
188
|
)
|
|
113
189
|
)
|
|
114
190
|
}
|
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeConfig.kt
CHANGED
|
@@ -2,10 +2,21 @@ package com.octopuscommunity.octopusreactnativesdk
|
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.ReadableMap
|
|
4
4
|
|
|
5
|
+
data class OctopusTextStyleConfig(
|
|
6
|
+
val fontType: String?, // "serif", "monospace", or "default"
|
|
7
|
+
val fontSize: Double? // Font size in points
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
data class OctopusFontsConfig(
|
|
11
|
+
val textStyles: Map<String, OctopusTextStyleConfig>?
|
|
12
|
+
)
|
|
13
|
+
|
|
5
14
|
data class OctopusThemeConfig(
|
|
6
15
|
val primaryColor: String?,
|
|
7
16
|
val primaryLowContrastColor: String?,
|
|
8
17
|
val primaryHighContrastColor: String?,
|
|
9
18
|
val onPrimaryColor: String?,
|
|
10
|
-
val logoSource: ReadableMap
|
|
19
|
+
val logoSource: ReadableMap?,
|
|
20
|
+
val colorScheme: String?, // "light" or "dark"
|
|
21
|
+
val fonts: OctopusFontsConfig?
|
|
11
22
|
)
|