@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.
Files changed (27) hide show
  1. package/README.md +308 -3
  2. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactNativeSdkModule.kt +82 -0
  3. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt +149 -1
  4. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeConfig.kt +22 -0
  5. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusThemeManager.kt +11 -0
  6. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt +234 -38
  7. package/ios/OctopusColorUtility.swift +44 -0
  8. package/ios/OctopusReactNativeSdk.mm +8 -0
  9. package/ios/OctopusReactNativeSdk.swift +52 -28
  10. package/ios/OctopusSDKInitializer.swift +136 -6
  11. package/ios/OctopusUIManager.swift +138 -7
  12. package/lib/module/initialize.js +54 -1
  13. package/lib/module/initialize.js.map +1 -1
  14. package/lib/module/internals/colorSchemeManager.js +105 -0
  15. package/lib/module/internals/colorSchemeManager.js.map +1 -0
  16. package/lib/module/internals/fontParser.js +48 -0
  17. package/lib/module/internals/fontParser.js.map +1 -0
  18. package/lib/typescript/src/initialize.d.ts +92 -0
  19. package/lib/typescript/src/initialize.d.ts.map +1 -1
  20. package/lib/typescript/src/internals/colorSchemeManager.d.ts +39 -0
  21. package/lib/typescript/src/internals/colorSchemeManager.d.ts.map +1 -0
  22. package/lib/typescript/src/internals/fontParser.d.ts +18 -0
  23. package/lib/typescript/src/internals/fontParser.d.ts.map +1 -0
  24. package/package.json +1 -1
  25. package/src/initialize.ts +131 -1
  26. package/src/internals/colorSchemeManager.ts +113 -0
  27. 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
- let hostingController = UIHostingController(rootView: octopusHomeScreen)
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
  }
@@ -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
- return OctopusReactNativeSdk.initialize(params);
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;;AAGhE;AACA;AACA;;AAsBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAACC,MAAwB,EAAiB;EAClE,OAAOF,qBAAqB,CAACC,UAAU,CAACC,MAAM,CAAC;AACjD","ignoreList":[]}
1
+ {"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":[]}