@octopus-community/react-native 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +308 -3
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactNativeSdkModule.kt +82 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt +149 -1
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeConfig.kt +22 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeManager.kt +11 -0
- package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt +234 -38
- package/ios/OctopusColorUtility.swift +44 -0
- package/ios/OctopusReactNativeSdk.mm +8 -0
- package/ios/OctopusReactNativeSdk.swift +52 -28
- package/ios/OctopusSDKInitializer.swift +136 -6
- package/ios/OctopusUIManager.swift +138 -7
- package/lib/module/initialize.js +54 -1
- package/lib/module/initialize.js.map +1 -1
- package/lib/module/internals/colorSchemeManager.js +105 -0
- package/lib/module/internals/colorSchemeManager.js.map +1 -0
- package/lib/module/internals/fontParser.js +48 -0
- package/lib/module/internals/fontParser.js.map +1 -0
- package/lib/typescript/src/initialize.d.ts +92 -0
- package/lib/typescript/src/initialize.d.ts.map +1 -1
- package/lib/typescript/src/internals/colorSchemeManager.d.ts +39 -0
- package/lib/typescript/src/internals/colorSchemeManager.d.ts.map +1 -0
- package/lib/typescript/src/internals/fontParser.d.ts +18 -0
- package/lib/typescript/src/internals/fontParser.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/initialize.ts +131 -1
- package/src/internals/colorSchemeManager.ts +113 -0
- package/src/internals/fontParser.ts +64 -0
package/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,13 +166,296 @@ 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
|
+
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
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { Image } from 'react-native';
|
|
177
|
+
|
|
178
|
+
await initialize({
|
|
179
|
+
apiKey: 'YOUR_OCTOPUS_API_KEY',
|
|
180
|
+
connectionMode: { type: 'octopus' },
|
|
181
|
+
theme: {
|
|
182
|
+
colors: {
|
|
183
|
+
primary: '#FF6B35', // Main brand color
|
|
184
|
+
primaryLowContrast: '#FF8C69', // Lighter variation of primary
|
|
185
|
+
primaryHighContrast: '#CC4A1A', // Darker variation for high contrast
|
|
186
|
+
onPrimary: '#FFFFFF', // Text color on primary background
|
|
187
|
+
},
|
|
188
|
+
logo: {
|
|
189
|
+
image: Image.resolveAssetSource(require('./assets/logo.png')),
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
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
|
|
361
|
+
|
|
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
|
|
424
|
+
|
|
425
|
+
**Note**: All theme properties are optional. If not provided, the default Octopus theme will be used.
|
|
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
|
+
|
|
147
429
|
### API docs
|
|
148
430
|
|
|
149
431
|
For details about the Typescript API, head to the [API docs](./docs/api/README.md).
|
|
150
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
|
+
|
|
151
439
|
### Example app
|
|
152
440
|
|
|
153
|
-
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.
|
|
154
459
|
|
|
155
460
|
## Troubleshooting
|
|
156
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
|
@@ -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,148 @@ class OctopusSDKInitializer {
|
|
|
29
35
|
promise.reject("INITIALIZE_ERROR", "Failed to initialize Octopus SDK", e)
|
|
30
36
|
}
|
|
31
37
|
}
|
|
38
|
+
|
|
39
|
+
fun parseThemeConfig(options: ReadableMap): OctopusThemeConfig? {
|
|
40
|
+
|
|
41
|
+
val themeMap = options.getMap("theme")
|
|
42
|
+
val colorScheme = options.getString("colorScheme") // Get colorScheme from React Native
|
|
43
|
+
|
|
44
|
+
var primaryColor: String? = null
|
|
45
|
+
var primaryLowContrastColor: String? = null
|
|
46
|
+
var primaryHighContrastColor: String? = null
|
|
47
|
+
var onPrimaryColor: String? = null
|
|
48
|
+
var logoSource: ReadableMap? = null
|
|
49
|
+
var fontsConfig: OctopusFontsConfig? = null
|
|
50
|
+
|
|
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
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
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
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Only create theme config if we have at least one customization
|
|
106
|
+
if (primaryColor != null || logoSource != null || colorScheme != null || fontsConfig != null) {
|
|
107
|
+
return OctopusThemeConfig(
|
|
108
|
+
primaryColor = primaryColor,
|
|
109
|
+
primaryLowContrastColor = primaryLowContrastColor,
|
|
110
|
+
primaryHighContrastColor = primaryHighContrastColor,
|
|
111
|
+
onPrimaryColor = onPrimaryColor,
|
|
112
|
+
logoSource = logoSource,
|
|
113
|
+
colorScheme = colorScheme,
|
|
114
|
+
fonts = fontsConfig
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
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
|
+
|
|
156
|
+
private fun extractResourceNameFromUri(uri: String): String? {
|
|
157
|
+
// Extract resource name from React Native image URI
|
|
158
|
+
// Examples: "logo.png" -> "logo", "images/logo.png" -> "logo"
|
|
159
|
+
return try {
|
|
160
|
+
val fileName = uri.substringAfterLast("/")
|
|
161
|
+
fileName.substringBeforeLast(".")
|
|
162
|
+
} catch (e: Exception) {
|
|
163
|
+
null
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private fun parseColor(colorString: String?): String? {
|
|
168
|
+
if (colorString == null) return null
|
|
169
|
+
|
|
170
|
+
return try {
|
|
171
|
+
// Validate that the color string is a valid hex color
|
|
172
|
+
Color.parseColor(colorString)
|
|
173
|
+
// Return the original string if parsing succeeds
|
|
174
|
+
colorString
|
|
175
|
+
} catch (e: IllegalArgumentException) {
|
|
176
|
+
// Invalid color format - return null to skip this color
|
|
177
|
+
null
|
|
178
|
+
}
|
|
179
|
+
}
|
|
32
180
|
|
|
33
181
|
private fun parseConnectionMode(options: ReadableMap): ConnectionMode {
|
|
34
182
|
val connectionModeMap = options.getMap("connectionMode")
|
|
@@ -36,7 +184,7 @@ class OctopusSDKInitializer {
|
|
|
36
184
|
"sso" -> {
|
|
37
185
|
ConnectionMode.SSO(
|
|
38
186
|
appManagedFields = ProfileFieldMapper.fromReactNativeArray(
|
|
39
|
-
connectionModeMap.getArray("appManagedFields")
|
|
187
|
+
if (connectionModeMap.hasKey("appManagedFields")) connectionModeMap.getArray("appManagedFields") else null
|
|
40
188
|
)
|
|
41
189
|
)
|
|
42
190
|
}
|
package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeConfig.kt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package com.octopuscommunity.octopusreactnativesdk
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap
|
|
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
|
+
|
|
14
|
+
data class OctopusThemeConfig(
|
|
15
|
+
val primaryColor: String?,
|
|
16
|
+
val primaryLowContrastColor: String?,
|
|
17
|
+
val primaryHighContrastColor: String?,
|
|
18
|
+
val onPrimaryColor: String?,
|
|
19
|
+
val logoSource: ReadableMap?,
|
|
20
|
+
val colorScheme: String?, // "light" or "dark"
|
|
21
|
+
val fonts: OctopusFontsConfig?
|
|
22
|
+
)
|
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
|
+
}
|