@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
|
@@ -1,22 +1,152 @@
|
|
|
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
|
+
var fonts: OctopusTheme.Fonts?
|
|
26
|
+
|
|
27
|
+
// Parse colors
|
|
28
|
+
if let colorsMap = themeMap["colors"] as? [String: Any] {
|
|
29
|
+
var primarySet: OctopusTheme.Colors.ColorSet?
|
|
30
|
+
|
|
31
|
+
// Check if this is a dual-mode theme (has light and dark properties)
|
|
32
|
+
if let lightColors = colorsMap["light"] as? [String: Any],
|
|
33
|
+
let darkColors = colorsMap["dark"] as? [String: Any] {
|
|
34
|
+
// Dual-mode theme - create adaptive colors that automatically respond to system appearance
|
|
35
|
+
if let lightPrimary = lightColors["primary"] as? String,
|
|
36
|
+
let darkPrimary = darkColors["primary"] as? String,
|
|
37
|
+
let lightPrimaryColor = OctopusColorUtility.color(fromHex: lightPrimary),
|
|
38
|
+
let darkPrimaryColor = OctopusColorUtility.color(fromHex: darkPrimary) {
|
|
39
|
+
|
|
40
|
+
// Create adaptive colors using UIColor's dynamic color capabilities
|
|
41
|
+
let adaptivePrimary = UIColor { traitCollection in
|
|
42
|
+
traitCollection.userInterfaceStyle == .dark ? darkPrimaryColor : lightPrimaryColor
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let lightLowContrast = (lightColors["primaryLowContrast"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? lightPrimaryColor
|
|
46
|
+
let darkLowContrast = (darkColors["primaryLowContrast"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? darkPrimaryColor
|
|
47
|
+
let adaptiveLowContrast = UIColor { traitCollection in
|
|
48
|
+
traitCollection.userInterfaceStyle == .dark ? darkLowContrast : lightLowContrast
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let lightHighContrast = (lightColors["primaryHighContrast"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? UIColor.black
|
|
52
|
+
let darkHighContrast = (darkColors["primaryHighContrast"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? UIColor.black
|
|
53
|
+
let adaptiveHighContrast = UIColor { traitCollection in
|
|
54
|
+
traitCollection.userInterfaceStyle == .dark ? darkHighContrast : lightHighContrast
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let lightOnPrimary = (lightColors["onPrimary"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? UIColor.white
|
|
58
|
+
let darkOnPrimary = (darkColors["onPrimary"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? UIColor.black
|
|
59
|
+
let adaptiveOnPrimary = UIColor { traitCollection in
|
|
60
|
+
traitCollection.userInterfaceStyle == .dark ? darkOnPrimary : lightOnPrimary
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
primarySet = OctopusTheme.Colors.ColorSet(
|
|
64
|
+
main: Color(uiColor: adaptivePrimary),
|
|
65
|
+
lowContrast: Color(uiColor: adaptiveLowContrast),
|
|
66
|
+
highContrast: Color(uiColor: adaptiveHighContrast)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
colors = OctopusTheme.Colors(
|
|
70
|
+
primarySet: primarySet,
|
|
71
|
+
onPrimary: Color(uiColor: adaptiveOnPrimary)
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
// Single-mode theme (backward compatibility)
|
|
76
|
+
if let primary = colorsMap["primary"] as? String,
|
|
77
|
+
let primaryColor = OctopusColorUtility.color(fromHex: primary) {
|
|
78
|
+
let lowContrast = (colorsMap["primaryLowContrast"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? primaryColor
|
|
79
|
+
let highContrast = (colorsMap["primaryHighContrast"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? UIColor.black
|
|
80
|
+
let onPrimary = (colorsMap["onPrimary"] as? String).flatMap(OctopusColorUtility.color(fromHex:)) ?? UIColor.white
|
|
81
|
+
|
|
82
|
+
primarySet = OctopusTheme.Colors.ColorSet(
|
|
83
|
+
main: Color(uiColor: primaryColor),
|
|
84
|
+
lowContrast: Color(uiColor: lowContrast),
|
|
85
|
+
highContrast: Color(uiColor: highContrast)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
colors = OctopusTheme.Colors(
|
|
89
|
+
primarySet: primarySet,
|
|
90
|
+
onPrimary: Color(uiColor: onPrimary)
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parse fonts
|
|
97
|
+
if let fontsMap = themeMap["fonts"] as? [String: Any] {
|
|
98
|
+
// Font customization will be applied through OctopusTypography
|
|
99
|
+
// This is parsed here but applied separately in the UI manager
|
|
100
|
+
fonts = OctopusTheme.Fonts()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Note: Logo will be handled separately in UI manager due to async loading requirements
|
|
104
|
+
// Only create theme if we have colors or fonts, otherwise return nil
|
|
105
|
+
guard colors != nil || fonts != nil else {
|
|
106
|
+
return nil
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return OctopusTheme(
|
|
110
|
+
colors: colors ?? OctopusTheme.Colors(),
|
|
111
|
+
fonts: fonts ?? OctopusTheme.Fonts(),
|
|
112
|
+
assets: OctopusTheme.Assets()
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func getLogoSource(from options: [String: Any]) -> [String: Any]? {
|
|
117
|
+
// First try to get logo from theme
|
|
118
|
+
if let themeMap = options["theme"] as? [String: Any],
|
|
119
|
+
let logoMap = themeMap["logo"] as? [String: Any],
|
|
120
|
+
let imageSource = logoMap["image"] as? [String: Any] {
|
|
121
|
+
return imageSource
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Fallback: try to get logo from root level (for backward compatibility)
|
|
125
|
+
if let logoMap = options["logo"] as? [String: Any],
|
|
126
|
+
let imageSource = logoMap["image"] as? [String: Any] {
|
|
127
|
+
return imageSource
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return nil
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
func getFontConfiguration(from options: [String: Any]) -> [String: Any]? {
|
|
134
|
+
guard let themeMap = options["theme"] as? [String: Any],
|
|
135
|
+
let fontsMap = themeMap["fonts"] as? [String: Any] else {
|
|
136
|
+
return nil
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Use pre-processed configuration from TypeScript layer
|
|
140
|
+
if let parsedConfig = fontsMap["parsedConfig"] as? [String: Any] {
|
|
141
|
+
return ["parsedConfig": parsedConfig]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return nil
|
|
145
|
+
}
|
|
146
|
+
|
|
17
147
|
private func parseConnectionMode(from connectionModeMap: [String: Any], eventManager: OctopusEventManager) throws -> ConnectionMode {
|
|
18
148
|
let connectionModeType = connectionModeMap["type"] as? String
|
|
19
|
-
|
|
149
|
+
|
|
20
150
|
switch connectionModeType {
|
|
21
151
|
case "sso":
|
|
22
152
|
return try createSSOConnectionMode(from: connectionModeMap, eventManager: eventManager)
|
|
@@ -26,10 +156,10 @@ class OctopusSDKInitializer {
|
|
|
26
156
|
throw InitializationError.invalidConnectionModeType
|
|
27
157
|
}
|
|
28
158
|
}
|
|
29
|
-
|
|
159
|
+
|
|
30
160
|
private func createSSOConnectionMode(from connectionModeMap: [String: Any], eventManager: OctopusEventManager) throws -> ConnectionMode {
|
|
31
161
|
let appManagedFields = ProfileFieldMapper.fromReactNativeArray(connectionModeMap["appManagedFields"] as? [String])
|
|
32
|
-
|
|
162
|
+
|
|
33
163
|
return .sso(
|
|
34
164
|
.init(
|
|
35
165
|
appManagedFields: appManagedFields,
|
|
@@ -48,7 +178,7 @@ enum InitializationError: Error, LocalizedError {
|
|
|
48
178
|
case missingAPIKey
|
|
49
179
|
case missingConnectionMode
|
|
50
180
|
case invalidConnectionModeType
|
|
51
|
-
|
|
181
|
+
|
|
52
182
|
var errorDescription: String? {
|
|
53
183
|
switch self {
|
|
54
184
|
case .missingAPIKey:
|
|
@@ -6,35 +6,166 @@ 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]?, fontConfiguration: [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
|
+
|
|
15
|
+
// Create custom theme with font configuration
|
|
16
|
+
let customTheme = createCustomTheme(baseTheme: theme, fontConfiguration: fontConfiguration)
|
|
14
17
|
|
|
15
18
|
let octopusHomeScreen = OctopusHomeScreen(octopus: octopus)
|
|
16
|
-
|
|
17
|
-
hostingController.modalPresentationStyle = .fullScreen
|
|
19
|
+
.environment(\.octopusTheme, customTheme)
|
|
18
20
|
|
|
21
|
+
let hostingController = UIHostingController(rootView: AnyView(octopusHomeScreen))
|
|
22
|
+
hostingController.modalPresentationStyle = .fullScreen
|
|
23
|
+
|
|
24
|
+
// Apply theme if provided
|
|
25
|
+
if let _ = theme {
|
|
26
|
+
// Apply theme immediately (without logo) to avoid delay
|
|
27
|
+
hostingController.rootView = AnyView(OctopusHomeScreen(octopus: octopus).environment(\.octopusTheme, customTheme))
|
|
28
|
+
|
|
29
|
+
// Then load logo asynchronously and update theme if logo loads
|
|
30
|
+
if let logoSource = logoSource {
|
|
31
|
+
loadLogo(from: logoSource) { [weak hostingController] logoImage in
|
|
32
|
+
DispatchQueue.main.async {
|
|
33
|
+
if let logoImage = logoImage {
|
|
34
|
+
let updatedTheme = OctopusUI.OctopusTheme(
|
|
35
|
+
colors: customTheme.colors,
|
|
36
|
+
fonts: customTheme.fonts,
|
|
37
|
+
assets: OctopusUI.OctopusTheme.Assets(logo: logoImage)
|
|
38
|
+
)
|
|
39
|
+
hostingController?.rootView = AnyView(OctopusHomeScreen(octopus: octopus).environment(\.octopusTheme, updatedTheme))
|
|
40
|
+
} else {
|
|
41
|
+
// Theme is already applied, no need to do anything
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} else if let logoSource = logoSource {
|
|
47
|
+
// No theme but there's a logo - load it and create a theme with just the logo
|
|
48
|
+
loadLogo(from: logoSource) { [weak hostingController] logoImage in
|
|
49
|
+
DispatchQueue.main.async {
|
|
50
|
+
if let logoImage = logoImage {
|
|
51
|
+
let logoTheme = OctopusUI.OctopusTheme(
|
|
52
|
+
colors: OctopusUI.OctopusTheme.Colors(),
|
|
53
|
+
fonts: OctopusUI.OctopusTheme.Fonts(),
|
|
54
|
+
assets: OctopusUI.OctopusTheme.Assets(logo: logoImage)
|
|
55
|
+
)
|
|
56
|
+
hostingController?.rootView = AnyView(OctopusHomeScreen(octopus: octopus).environment(\.octopusTheme, logoTheme))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
19
62
|
presentedViewController = hostingController
|
|
20
|
-
|
|
63
|
+
|
|
21
64
|
presentingViewController.present(hostingController, animated: true)
|
|
22
65
|
}
|
|
23
66
|
|
|
67
|
+
private func loadLogo(from source: [String: Any], completion: @escaping (UIImage?) -> Void) {
|
|
68
|
+
// Handle React Native image source with URI (from Image.resolveAssetSource)
|
|
69
|
+
guard let uri = source["uri"] as? String else {
|
|
70
|
+
completion(nil)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Remote URL
|
|
75
|
+
if uri.hasPrefix("http") {
|
|
76
|
+
guard let url = URL(string: uri) else {
|
|
77
|
+
completion(nil)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
URLSession.shared.dataTask(with: url) { data, response, error in
|
|
82
|
+
guard let data = data, error == nil else {
|
|
83
|
+
completion(nil)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let image = UIImage(data: data)
|
|
88
|
+
completion(image)
|
|
89
|
+
}.resume()
|
|
90
|
+
} else {
|
|
91
|
+
// Local file path (from Image.resolveAssetSource)
|
|
92
|
+
if let image = UIImage(contentsOfFile: uri) {
|
|
93
|
+
completion(image)
|
|
94
|
+
} else {
|
|
95
|
+
// Fallback: try to load from bundle using the filename
|
|
96
|
+
let filename = (uri as NSString).lastPathComponent
|
|
97
|
+
let nameWithoutExtension = (filename as NSString).deletingPathExtension
|
|
98
|
+
let image = UIImage(named: nameWithoutExtension)
|
|
99
|
+
completion(image)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
24
104
|
func closeUI() throws {
|
|
25
105
|
guard let presentedVC = presentedViewController else {
|
|
26
106
|
throw NSError(domain: "CLOSE_UI_ERROR", code: 0, userInfo: [NSLocalizedDescriptionKey: "No UI is currently presented"])
|
|
27
107
|
}
|
|
28
|
-
|
|
108
|
+
|
|
29
109
|
presentedVC.dismiss(animated: true) {
|
|
30
110
|
self.presentedViewController = nil
|
|
31
111
|
}
|
|
32
112
|
}
|
|
33
|
-
|
|
113
|
+
|
|
34
114
|
func cleanup() {
|
|
35
115
|
if let presentedVC = presentedViewController {
|
|
36
116
|
presentedVC.dismiss(animated: false, completion: nil)
|
|
37
117
|
presentedViewController = nil
|
|
38
118
|
}
|
|
39
119
|
}
|
|
120
|
+
|
|
121
|
+
private func createCustomTheme(baseTheme: OctopusUI.OctopusTheme?, fontConfiguration: [String: Any]?) -> OctopusUI.OctopusTheme {
|
|
122
|
+
// If no font configuration, return the base theme or default
|
|
123
|
+
guard let fontConfig = fontConfiguration else {
|
|
124
|
+
return baseTheme ?? OctopusUI.OctopusTheme()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Use pre-processed configuration from TypeScript layer
|
|
128
|
+
if let parsedConfig = fontConfig["parsedConfig"] as? [String: Any],
|
|
129
|
+
let textStyles = parsedConfig["textStyles"] as? [String: [String: Any]] {
|
|
130
|
+
|
|
131
|
+
let customFonts = OctopusUI.OctopusTheme.Fonts(
|
|
132
|
+
title1: createFontFromPreProcessedStyle(textStyles["title1"], defaultSize: 28),
|
|
133
|
+
title2: createFontFromPreProcessedStyle(textStyles["title2"], defaultSize: 24),
|
|
134
|
+
body1: createFontFromPreProcessedStyle(textStyles["body1"], defaultSize: 18),
|
|
135
|
+
body2: createFontFromPreProcessedStyle(textStyles["body2"], defaultSize: 16),
|
|
136
|
+
caption1: createFontFromPreProcessedStyle(textStyles["caption1"], defaultSize: 12),
|
|
137
|
+
caption2: createFontFromPreProcessedStyle(textStyles["caption2"], defaultSize: 10),
|
|
138
|
+
navBarItem: createFontFromPreProcessedStyle(textStyles["body1"], defaultSize: 17) // Use body1 style for nav
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return OctopusUI.OctopusTheme(
|
|
142
|
+
colors: baseTheme?.colors ?? OctopusUI.OctopusTheme.Colors(),
|
|
143
|
+
fonts: customFonts,
|
|
144
|
+
assets: baseTheme?.assets ?? OctopusUI.OctopusTheme.Assets()
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return baseTheme ?? OctopusUI.OctopusTheme()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private func createFontFromPreProcessedStyle(_ textStyle: [String: Any]?, defaultSize: CGFloat) -> Font {
|
|
152
|
+
guard let textStyle = textStyle else {
|
|
153
|
+
return Font.system(size: defaultSize)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let fontType = textStyle["fontType"] as? String
|
|
157
|
+
let fontSize = textStyle["fontSize"] as? Double ?? Double(defaultSize)
|
|
158
|
+
|
|
159
|
+
switch fontType {
|
|
160
|
+
case "serif":
|
|
161
|
+
return Font.system(size: CGFloat(fontSize), design: .serif)
|
|
162
|
+
case "monospace":
|
|
163
|
+
return Font.system(size: CGFloat(fontSize), design: .monospaced)
|
|
164
|
+
case "default":
|
|
165
|
+
return Font.system(size: CGFloat(fontSize))
|
|
166
|
+
default:
|
|
167
|
+
return Font.system(size: CGFloat(fontSize))
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
40
171
|
}
|
package/lib/module/initialize.js
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { OctopusReactNativeSdk } from "./internals/nativeModule.js";
|
|
4
|
+
import { Appearance } from 'react-native';
|
|
5
|
+
import { colorSchemeManager } from "./internals/colorSchemeManager.js";
|
|
6
|
+
import { parseFontConfig } from "./internals/fontParser.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Color set for a specific appearance mode (light or dark).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Font type options for unified cross-platform font configuration.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Font size configuration for different text types.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Font configuration for a specific text type.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Font configuration for customizing the Octopus UI typography.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Theme configuration for customizing the Octopus UI appearance.
|
|
30
|
+
*
|
|
31
|
+
* Supports two approaches:
|
|
32
|
+
* 1. Single color set (backward compatible) - colors are applied to both light and dark modes
|
|
33
|
+
* 2. Dual mode colors - separate color sets for light and dark modes
|
|
34
|
+
*/
|
|
4
35
|
|
|
5
36
|
/**
|
|
6
37
|
* Configuration params for initializing the Octopus SDK.
|
|
@@ -33,6 +64,28 @@ import { OctopusReactNativeSdk } from "./internals/nativeModule.js";
|
|
|
33
64
|
* ```
|
|
34
65
|
*/
|
|
35
66
|
export function initialize(params) {
|
|
36
|
-
|
|
67
|
+
// Automatically detect the current color scheme and add it to the params
|
|
68
|
+
const colorScheme = Appearance.getColorScheme();
|
|
69
|
+
|
|
70
|
+
// Pre-process font configuration to avoid duplication in native layers
|
|
71
|
+
const processedTheme = params.theme ? {
|
|
72
|
+
...params.theme,
|
|
73
|
+
fonts: params.theme.fonts ? {
|
|
74
|
+
...params.theme.fonts,
|
|
75
|
+
parsedConfig: parseFontConfig(params.theme.fonts)
|
|
76
|
+
} : undefined
|
|
77
|
+
} : undefined;
|
|
78
|
+
const paramsWithColorScheme = {
|
|
79
|
+
...params,
|
|
80
|
+
theme: processedTheme,
|
|
81
|
+
colorScheme: colorScheme || undefined
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Set the theme in the color scheme manager for dual-mode support
|
|
85
|
+
colorSchemeManager.setTheme(processedTheme || null);
|
|
86
|
+
|
|
87
|
+
// Start listening to color scheme changes for automatic updates
|
|
88
|
+
colorSchemeManager.startListening();
|
|
89
|
+
return OctopusReactNativeSdk.initialize(paramsWithColorScheme);
|
|
37
90
|
}
|
|
38
91
|
//# sourceMappingURL=initialize.js.map
|
|
@@ -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","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":[]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Appearance, AppState } from 'react-native';
|
|
4
|
+
import { OctopusReactNativeSdk } from "./nativeModule.js";
|
|
5
|
+
let isListening = false;
|
|
6
|
+
let currentColorScheme = null;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Internal color scheme manager that automatically handles appearance changes
|
|
10
|
+
* and updates the native modules accordingly.
|
|
11
|
+
*/
|
|
12
|
+
export class ColorSchemeManager {
|
|
13
|
+
appearanceSubscription = null;
|
|
14
|
+
appStateSubscription = null;
|
|
15
|
+
constructor() {}
|
|
16
|
+
static getInstance() {
|
|
17
|
+
if (!ColorSchemeManager.instance) {
|
|
18
|
+
ColorSchemeManager.instance = new ColorSchemeManager();
|
|
19
|
+
}
|
|
20
|
+
return ColorSchemeManager.instance;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Set the current theme for color scheme management
|
|
25
|
+
* Note: This is kept for API compatibility but theme updates are not used
|
|
26
|
+
* when the Octopus UI is open since the React Native app is backgrounded.
|
|
27
|
+
*/
|
|
28
|
+
setTheme(_theme) {
|
|
29
|
+
// Theme is set during initialization and applied when UI opens
|
|
30
|
+
// No need to store it here since updates can't happen while UI is open
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Start listening to appearance changes and automatically update native modules
|
|
35
|
+
*/
|
|
36
|
+
startListening() {
|
|
37
|
+
if (isListening) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
isListening = true;
|
|
41
|
+
currentColorScheme = Appearance.getColorScheme();
|
|
42
|
+
|
|
43
|
+
// Listen to appearance changes
|
|
44
|
+
this.appearanceSubscription = Appearance.addChangeListener(({
|
|
45
|
+
colorScheme
|
|
46
|
+
}) => {
|
|
47
|
+
currentColorScheme = colorScheme;
|
|
48
|
+
this.updateNativeColorScheme();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Listen to app state changes to ensure we have the latest color scheme
|
|
52
|
+
this.appStateSubscription = AppState.addEventListener('change', nextAppState => {
|
|
53
|
+
// Only check color scheme when app becomes active (foreground)
|
|
54
|
+
if (nextAppState === 'active') {
|
|
55
|
+
const newColorScheme = Appearance.getColorScheme();
|
|
56
|
+
if (newColorScheme !== currentColorScheme) {
|
|
57
|
+
currentColorScheme = newColorScheme;
|
|
58
|
+
this.updateNativeColorScheme();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Stop listening to appearance changes
|
|
66
|
+
*/
|
|
67
|
+
stopListening() {
|
|
68
|
+
if (this.appearanceSubscription) {
|
|
69
|
+
this.appearanceSubscription.remove();
|
|
70
|
+
this.appearanceSubscription = null;
|
|
71
|
+
}
|
|
72
|
+
if (this.appStateSubscription) {
|
|
73
|
+
this.appStateSubscription.remove();
|
|
74
|
+
this.appStateSubscription = null;
|
|
75
|
+
}
|
|
76
|
+
isListening = false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get the current color scheme
|
|
81
|
+
*/
|
|
82
|
+
getCurrentColorScheme() {
|
|
83
|
+
return currentColorScheme;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Update the native modules with the current color scheme
|
|
88
|
+
* Note: This is only called when the app becomes active (foreground).
|
|
89
|
+
* When the Octopus UI is open, the React Native app is backgrounded,
|
|
90
|
+
* so theme updates are not possible during UI display.
|
|
91
|
+
*/
|
|
92
|
+
updateNativeColorScheme() {
|
|
93
|
+
// Both platforms handle theme updates when the app becomes active
|
|
94
|
+
// iOS uses adaptive colors, Android applies the theme when UI reopens
|
|
95
|
+
try {
|
|
96
|
+
OctopusReactNativeSdk.updateColorScheme(currentColorScheme || undefined);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.stopListening();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Export singleton instance
|
|
104
|
+
export const colorSchemeManager = ColorSchemeManager.getInstance();
|
|
105
|
+
//# sourceMappingURL=colorSchemeManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["Appearance","AppState","OctopusReactNativeSdk","isListening","currentColorScheme","ColorSchemeManager","appearanceSubscription","appStateSubscription","constructor","getInstance","instance","setTheme","_theme","startListening","getColorScheme","addChangeListener","colorScheme","updateNativeColorScheme","addEventListener","nextAppState","newColorScheme","stopListening","remove","getCurrentColorScheme","updateColorScheme","undefined","error","colorSchemeManager"],"sourceRoot":"../../../src","sources":["internals/colorSchemeManager.ts"],"mappings":";;AAAA,SAASA,UAAU,EAAEC,QAAQ,QAAQ,cAAc;AACnD,SAASC,qBAAqB,QAAQ,mBAAgB;AAGtD,IAAIC,WAAW,GAAG,KAAK;AACvB,IAAIC,kBAA6C,GAAG,IAAI;;AAExD;AACA;AACA;AACA;AACA,OAAO,MAAMC,kBAAkB,CAAC;EAEtBC,sBAAsB,GAAQ,IAAI;EAClCC,oBAAoB,GAAQ,IAAI;EAEhCC,WAAWA,CAAA,EAAG,CAAC;EAEvB,OAAOC,WAAWA,CAAA,EAAuB;IACvC,IAAI,CAACJ,kBAAkB,CAACK,QAAQ,EAAE;MAChCL,kBAAkB,CAACK,QAAQ,GAAG,IAAIL,kBAAkB,CAAC,CAAC;IACxD;IACA,OAAOA,kBAAkB,CAACK,QAAQ;EACpC;;EAEA;AACF;AACA;AACA;AACA;EACEC,QAAQA,CAACC,MAA2B,EAAQ;IAC1C;IACA;EAAA;;EAGF;AACF;AACA;EACEC,cAAcA,CAAA,EAAS;IACrB,IAAIV,WAAW,EAAE;MACf;IACF;IAEAA,WAAW,GAAG,IAAI;IAClBC,kBAAkB,GAAGJ,UAAU,CAACc,cAAc,CAAC,CAAC;;IAEhD;IACA,IAAI,CAACR,sBAAsB,GAAGN,UAAU,CAACe,iBAAiB,CACxD,CAAC;MAAEC;IAAY,CAAC,KAAK;MACnBZ,kBAAkB,GAAGY,WAAW;MAChC,IAAI,CAACC,uBAAuB,CAAC,CAAC;IAChC,CACF,CAAC;;IAED;IACA,IAAI,CAACV,oBAAoB,GAAGN,QAAQ,CAACiB,gBAAgB,CACnD,QAAQ,EACPC,YAAY,IAAK;MAChB;MACA,IAAIA,YAAY,KAAK,QAAQ,EAAE;QAC7B,MAAMC,cAAc,GAAGpB,UAAU,CAACc,cAAc,CAAC,CAAC;QAClD,IAAIM,cAAc,KAAKhB,kBAAkB,EAAE;UACzCA,kBAAkB,GAAGgB,cAAc;UACnC,IAAI,CAACH,uBAAuB,CAAC,CAAC;QAChC;MACF;IACF,CACF,CAAC;EACH;;EAEA;AACF;AACA;EACEI,aAAaA,CAAA,EAAS;IACpB,IAAI,IAAI,CAACf,sBAAsB,EAAE;MAC/B,IAAI,CAACA,sBAAsB,CAACgB,MAAM,CAAC,CAAC;MACpC,IAAI,CAAChB,sBAAsB,GAAG,IAAI;IACpC;IAEA,IAAI,IAAI,CAACC,oBAAoB,EAAE;MAC7B,IAAI,CAACA,oBAAoB,CAACe,MAAM,CAAC,CAAC;MAClC,IAAI,CAACf,oBAAoB,GAAG,IAAI;IAClC;IAEAJ,WAAW,GAAG,KAAK;EACrB;;EAEA;AACF;AACA;EACEoB,qBAAqBA,CAAA,EAA8B;IACjD,OAAOnB,kBAAkB;EAC3B;;EAEA;AACF;AACA;AACA;AACA;AACA;EACUa,uBAAuBA,CAAA,EAAS;IACtC;IACA;IACA,IAAI;MACFf,qBAAqB,CAACsB,iBAAiB,CAACpB,kBAAkB,IAAIqB,SAAS,CAAC;IAC1E,CAAC,CAAC,OAAOC,KAAK,EAAE;MACd,IAAI,CAACL,aAAa,CAAC,CAAC;IACtB;EACF;AACF;;AAEA;AACA,OAAO,MAAMM,kBAAkB,GAAGtB,kBAAkB,CAACI,WAAW,CAAC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parsed font configuration for native platforms
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse font configuration from the theme and return a simplified structure
|
|
9
|
+
* that can be easily consumed by native platforms.
|
|
10
|
+
*
|
|
11
|
+
* This centralizes the font parsing logic to avoid duplication across platforms.
|
|
12
|
+
*/
|
|
13
|
+
export function parseFontConfig(fonts) {
|
|
14
|
+
if (!fonts?.textStyles) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const textStyles = {};
|
|
18
|
+
|
|
19
|
+
// Define the text style keys that are supported
|
|
20
|
+
const supportedTextStyles = ['title1', 'title2', 'body1', 'body2', 'caption1', 'caption2'];
|
|
21
|
+
for (const key of supportedTextStyles) {
|
|
22
|
+
const textStyle = fonts.textStyles[key];
|
|
23
|
+
if (textStyle) {
|
|
24
|
+
const parsedStyle = {};
|
|
25
|
+
|
|
26
|
+
// Parse font type
|
|
27
|
+
if (textStyle.fontType) {
|
|
28
|
+
parsedStyle.fontType = textStyle.fontType;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Parse font size
|
|
32
|
+
if (textStyle.fontSize?.size) {
|
|
33
|
+
parsedStyle.fontSize = textStyle.fontSize.size;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Only include the style if it has at least one property
|
|
37
|
+
if (parsedStyle.fontType || parsedStyle.fontSize) {
|
|
38
|
+
textStyles[key] = parsedStyle;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Return null if no text styles were parsed
|
|
44
|
+
return Object.keys(textStyles).length > 0 ? {
|
|
45
|
+
textStyles
|
|
46
|
+
} : null;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=fontParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["parseFontConfig","fonts","textStyles","supportedTextStyles","key","textStyle","parsedStyle","fontType","fontSize","size","Object","keys","length"],"sourceRoot":"../../../src","sources":["internals/fontParser.ts"],"mappings":";;AAEA;AACA;AACA;;AAWA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASA,eAAeA,CAACC,KAAoB,EAA2B;EAC7E,IAAI,CAACA,KAAK,EAAEC,UAAU,EAAE;IACtB,OAAO,IAAI;EACb;EAEA,MAAMA,UAAoE,GACxE,CAAC,CAAC;;EAEJ;EACA,MAAMC,mBAAmB,GAAG,CAC1B,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,OAAO,EACP,UAAU,EACV,UAAU,CACX;EAED,KAAK,MAAMC,GAAG,IAAID,mBAAmB,EAAE;IACrC,MAAME,SAAS,GAAGJ,KAAK,CAACC,UAAU,CAACE,GAAG,CAAkC;IACxE,IAAIC,SAAS,EAAE;MACb,MAAMC,WAAqD,GAAG,CAAC,CAAC;;MAEhE;MACA,IAAID,SAAS,CAACE,QAAQ,EAAE;QACtBD,WAAW,CAACC,QAAQ,GAAGF,SAAS,CAACE,QAAQ;MAC3C;;MAEA;MACA,IAAIF,SAAS,CAACG,QAAQ,EAAEC,IAAI,EAAE;QAC5BH,WAAW,CAACE,QAAQ,GAAGH,SAAS,CAACG,QAAQ,CAACC,IAAI;MAChD;;MAEA;MACA,IAAIH,WAAW,CAACC,QAAQ,IAAID,WAAW,CAACE,QAAQ,EAAE;QAChDN,UAAU,CAACE,GAAG,CAAC,GAAGE,WAAW;MAC/B;IACF;EACF;;EAEA;EACA,OAAOI,MAAM,CAACC,IAAI,CAACT,UAAU,CAAC,CAACU,MAAM,GAAG,CAAC,GAAG;IAAEV;EAAW,CAAC,GAAG,IAAI;AACnE","ignoreList":[]}
|