react-native-pfm-sdk-react-native 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Eslam Ali
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "PfmSdkReactNative"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+ s.swift_version = "5.0"
13
+
14
+ s.platforms = { :ios => "13.0" }
15
+
16
+ s.source = { :git => "https://github.com/eslam-ali-ios-lune/react-native-pfm-sdk-react-native.git", :tag => "#{s.version}" }
17
+
18
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
19
+ s.private_header_files = "ios/**/*.h"
20
+
21
+ install_modules_dependencies(s)
22
+
23
+ s.dependency "LuneSDK", "0.7.4"
24
+ end
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # react-native-pfm-sdk-react-native
2
+
3
+ React native plugin that intergates with native iOS and Android SDKs
4
+
5
+ ## Installation
6
+
7
+
8
+ ```sh
9
+ npm install react-native-pfm-sdk-react-native
10
+ ```
11
+
12
+
13
+ ## Usage
14
+
15
+
16
+ ```js
17
+ import { multiply } from 'react-native-pfm-sdk-react-native';
18
+
19
+ // ...
20
+
21
+ const result = multiply(3, 7);
22
+ ```
23
+
24
+
25
+ ## Contributing
26
+
27
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
28
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
29
+ - [Code of conduct](CODE_OF_CONDUCT.md)
30
+
31
+ ## License
32
+
33
+ MIT
34
+
35
+ ---
36
+
37
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,83 @@
1
+ buildscript {
2
+ ext.PfmSdkReactNative = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36,
6
+ targetSdkVersion: 36
7
+ ]
8
+
9
+ ext.getExtOrDefault = { prop ->
10
+ if (rootProject.ext.has(prop)) {
11
+ return rootProject.ext.get(prop)
12
+ }
13
+ return PfmSdkReactNative[prop]
14
+ }
15
+
16
+ repositories {
17
+ google()
18
+ mavenCentral()
19
+ }
20
+
21
+ dependencies {
22
+ classpath "com.android.tools.build:gradle:8.7.2"
23
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
24
+ // 🚀 Correct: This is a build tool plugin
25
+ classpath "org.jetbrains.kotlin:compose-compiler-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
26
+ }
27
+ }
28
+
29
+ apply plugin: "com.android.library"
30
+ apply plugin: "kotlin-android"
31
+ apply plugin: "com.facebook.react"
32
+ apply plugin: "org.jetbrains.kotlin.plugin.compose"
33
+
34
+ android {
35
+ namespace "com.pfmsdkreactnative"
36
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
37
+
38
+ defaultConfig {
39
+ minSdkVersion getExtOrDefault("minSdkVersion")
40
+ targetSdkVersion getExtOrDefault("targetSdkVersion")
41
+ }
42
+
43
+ buildFeatures {
44
+ buildConfig true
45
+ compose true
46
+ }
47
+
48
+ buildTypes {
49
+ release {
50
+ minifyEnabled false
51
+ }
52
+ }
53
+
54
+ lint {
55
+ disable "GradleCompatible"
56
+ }
57
+
58
+ compileOptions {
59
+ sourceCompatibility JavaVersion.VERSION_17
60
+ targetCompatibility JavaVersion.VERSION_17
61
+ }
62
+
63
+ kotlinOptions {
64
+ jvmTarget = "17"
65
+ }
66
+ }
67
+
68
+ dependencies {
69
+ implementation "com.facebook.react:react-android"
70
+
71
+ // 🚀 1. Lune SDK
72
+ implementation("io.lunedata.lunesdk:lunesdk:0.3.14")
73
+
74
+ // 🚀 2. Compose BOM & Core UI
75
+ def composeBom = platform('androidx.compose:compose-bom:2024.02.00')
76
+ implementation composeBom
77
+ implementation 'androidx.compose.ui:ui'
78
+ implementation 'androidx.compose.material3:material3' // Moved from buildscript to here
79
+ implementation 'androidx.compose.foundation:foundation'
80
+
81
+ // 🚀 3. Required for Activity-based rendering
82
+ implementation 'androidx.activity:activity-compose:1.8.2'
83
+ }
@@ -0,0 +1,10 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.pfmsdkreactnative">
3
+
4
+ <uses-permission android:name="android.permission.INTERNET" />
5
+
6
+ <application>
7
+ <activity android:name=".LuneActivity" />
8
+ </application>
9
+
10
+ </manifest>
@@ -0,0 +1,50 @@
1
+ package com.pfmsdkreactnative
2
+
3
+ import android.os.Bundle
4
+ import androidx.activity.ComponentActivity
5
+ import androidx.activity.compose.setContent
6
+ import androidx.compose.material3.ExperimentalMaterial3Api
7
+ import androidx.compose.material3.ModalBottomSheet
8
+ import androidx.compose.material3.rememberModalBottomSheetState
9
+ import androidx.compose.ui.graphics.Color
10
+ import androidx.compose.material3.MaterialTheme
11
+
12
+ // 🚀 FIXED IMPORT from your Cordova snippet
13
+ import io.lunedata.lunesdk.library.classes.LuneSDKManager
14
+
15
+ class LuneActivity : ComponentActivity() {
16
+ @OptIn(ExperimentalMaterial3Api::class)
17
+ override fun onCreate(savedInstanceState: Bundle?) {
18
+ super.onCreate(savedInstanceState)
19
+
20
+ val rawUrl = intent.getStringExtra("baseUrl") ?: ""
21
+ val baseUrl = if (rawUrl.endsWith("/")) rawUrl else "$rawUrl/"
22
+ val token = intent.getStringExtra("token") ?: ""
23
+ val viewType = intent.getStringExtra("viewType") ?: ""
24
+
25
+ // 🚀 Initialize SDK with the static refresher from your Module
26
+ val sdk = LuneSDKManager(
27
+ baseUrl = baseUrl,
28
+ token = token,
29
+ refreshTokenCallback = PfmSdkReactNativeModule.tokenRefresher
30
+ )
31
+
32
+ setContent {
33
+ MaterialTheme {
34
+ val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
35
+ ModalBottomSheet(
36
+ onDismissRequest = { finish() },
37
+ sheetState = sheetState,
38
+ containerColor = Color.White
39
+ ) {
40
+ when (viewType) {
41
+ "cashflow" -> sdk.CashflowComponent()
42
+ "budget" -> sdk.BudgetComponent()
43
+ "spend" -> sdk.ExpenseComponent()
44
+ else -> sdk.ExpenseComponent()
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,99 @@
1
+ package com.pfmsdkreactnative
2
+
3
+ import android.content.Intent
4
+ import android.util.Log
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.module.annotations.ReactModule
8
+ import com.facebook.react.modules.core.DeviceEventManagerModule
9
+ import io.lunedata.lunesdk.library.classes.RefreshTokenCallback // 🚀 Import Refresher
10
+ import java.util.concurrent.CountDownLatch
11
+
12
+ @ReactModule(name = PfmSdkReactNativeModule.NAME)
13
+ class PfmSdkReactNativeModule(private val reactContext: ReactApplicationContext) :
14
+ NativePfmSdkReactNativeSpec(reactContext) {
15
+
16
+ companion object {
17
+ const val NAME = "PfmSdkReactNative"
18
+ var savedBaseUrl: String = ""
19
+ var savedToken: String = ""
20
+
21
+ // 🚦 Latch logic for Token Refresh
22
+ var tokenLatch: CountDownLatch? = null
23
+ var newTokenResponse: String? = null
24
+
25
+ val tokenRefresher = object : RefreshTokenCallback {
26
+ override fun refreshToken(): String? {
27
+ tokenLatch = CountDownLatch(1)
28
+ newTokenResponse = null
29
+
30
+ // 📢 Notify React Native that we need a token
31
+ // This maps to the 'onRefreshToken' listener in JS
32
+ instance?.sendEvent("onRefreshToken", null)
33
+
34
+ try {
35
+ tokenLatch?.await() // 🚦 Pause thread until JS calls submitNewToken
36
+ } catch (e: InterruptedException) {
37
+ Log.e("LuneSDK", "Token wait interrupted", e)
38
+ }
39
+ return newTokenResponse
40
+ }
41
+ }
42
+
43
+ private var instance: PfmSdkReactNativeModule? = null
44
+ }
45
+
46
+ init { instance = this }
47
+
48
+ override fun getName() = NAME
49
+
50
+ override fun initializeSdk(baseUrl: String, accessToken: String, promise: Promise) {
51
+ savedBaseUrl = baseUrl
52
+ savedToken = accessToken
53
+ promise.resolve(null)
54
+ }
55
+
56
+ // 🚀 Call this from JS when you have the new token
57
+ override fun registerRefreshTokenCallback(promise: Promise) {
58
+ // In TurboModules, we use this to signify JS is ready to listen
59
+ promise.resolve(null)
60
+ }
61
+
62
+ // Use this to pass the token back from JS
63
+ fun submitNewToken(token: String?) {
64
+ newTokenResponse = token
65
+ if (token != null) savedToken = token
66
+ tokenLatch?.countDown() // 🟢 Resume the SDK thread
67
+ }
68
+
69
+ override fun showLuneView(viewIdentifier: String, promise: Promise) {
70
+ val currentActivity = reactContext.currentActivity
71
+ if (currentActivity == null) {
72
+ promise.reject("ERROR", "No Activity")
73
+ return
74
+ }
75
+
76
+ val intent = Intent(currentActivity, LuneActivity::class.java).apply {
77
+ putExtra("viewType", viewIdentifier)
78
+ putExtra("baseUrl", savedBaseUrl)
79
+ putExtra("token", savedToken)
80
+ }
81
+ currentActivity.startActivity(intent)
82
+ promise.resolve(null)
83
+ }
84
+
85
+ private fun sendEvent(eventName: String, params: String?) {
86
+ reactContext
87
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
88
+ .emit(eventName, params)
89
+ }
90
+
91
+ // Boilerplate for other methods...
92
+ override fun optInForEventListener(promise: Promise) { promise.resolve(null) }
93
+ override fun optInForErrorLogger(promise: Promise) { promise.resolve(null) }
94
+ override fun optInForErrorNotifier(promise: Promise) { promise.resolve(null) }
95
+ override fun optInForSuccessNotifier(promise: Promise) { promise.resolve(null) }
96
+ override fun destroySdk(promise: Promise) { promise.resolve(null) }
97
+ override fun addListener(eventName: String) {}
98
+ override fun removeListeners(count: Double) {}
99
+ }
@@ -0,0 +1,31 @@
1
+ package com.pfmsdkreactnative
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class PfmSdkReactNativePackage : BaseReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == PfmSdkReactNativeModule.NAME) {
13
+ PfmSdkReactNativeModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
20
+ mapOf(
21
+ PfmSdkReactNativeModule.NAME to ReactModuleInfo(
22
+ name = PfmSdkReactNativeModule.NAME,
23
+ className = PfmSdkReactNativeModule.NAME,
24
+ canOverrideExistingModule = false,
25
+ needsEagerInit = false,
26
+ isCxxModule = false,
27
+ isTurboModule = true
28
+ )
29
+ )
30
+ }
31
+ }
@@ -0,0 +1,48 @@
1
+ import SwiftUI
2
+ import LuneSDK
3
+
4
+ public struct LuneSelectorView: View {
5
+ public let viewType: String
6
+ public let baseUrl: String
7
+ public let token: String
8
+
9
+ // Initialize the manager using the props passed from React Native
10
+ var sdkManager: LuneSDKManager {
11
+ LuneSDKManager(baseUrl: baseUrl, token: token)
12
+ }
13
+
14
+ public init(viewType: String, baseUrl: String, token: String) {
15
+ self.viewType = viewType
16
+ self.baseUrl = baseUrl
17
+ self.token = token
18
+ }
19
+
20
+ // @ViewBuilder is required when a switch statement returns different view types
21
+ @ViewBuilder
22
+ public var body: some View {
23
+ switch viewType {
24
+ case "cashflow":
25
+ sdkManager.CashflowComponent()
26
+ case "spend":
27
+ sdkManager.ExpenseComponent()
28
+ case "budget":
29
+ sdkManager.BudgetComponent()
30
+ case "category_spend_chart":
31
+ sdkManager.CategorySpendChartComponent()
32
+ case "cashflow_chart":
33
+ sdkManager.CashflowChartComponent()
34
+ case "brand_trend_chart":
35
+ sdkManager.BrandTrendChartComponent()
36
+ case "category_trend_chart":
37
+ sdkManager.CategoryTrendChartComponent()
38
+ case "transaction_list":
39
+ sdkManager.TransactionListComponent()
40
+ case "brand_trend":
41
+ sdkManager.BrandTrendsComponent()
42
+ case "category_trend":
43
+ sdkManager.CategoryTrendsComponent()
44
+ default:
45
+ Text("Unknown View Type: \(viewType)")
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,22 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ // If you are using the New Architecture, we include the spec
5
+ #ifdef RCT_NEW_ARCH_ENABLED
6
+ #import "RNPfmSdkReactNativeSpec.h"
7
+ #endif
8
+
9
+ // We define the interface. Note that it ALWAYS inherits from RCTEventEmitter
10
+ @interface PfmSdkReactNative : RCTEventEmitter <
11
+ #ifdef RCT_NEW_ARCH_ENABLED
12
+ NativePfmSdkReactNativeSpec
13
+ #else
14
+ RCTBridgeModule
15
+ #endif
16
+ >
17
+
18
+ // This declaration is the most important part.
19
+ // It tells the .mm file that this method exists in the parent class.
20
+ - (void)sendEventWithName:(NSString *)name body:(id)body;
21
+
22
+ @end
@@ -0,0 +1,90 @@
1
+ #import "PfmSdkReactNative.h"
2
+
3
+ // This magic line imports your Swift code into Objective-C
4
+ #if __has_include("PfmSdkReactNative-Swift.h")
5
+ #import "PfmSdkReactNative-Swift.h"
6
+ #else
7
+ #import <PfmSdkReactNative/PfmSdkReactNative-Swift.h>
8
+ #endif
9
+
10
+ @implementation PfmSdkReactNative
11
+
12
+ RCT_EXPORT_MODULE()
13
+
14
+ // --- TURBOMODULE METHOD ---
15
+ #ifdef RCT_NEW_ARCH_ENABLED
16
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
17
+ (const facebook::react::ObjCTurboModule::InitParams &)params
18
+ {
19
+ return std::make_shared<facebook::react::NativePfmSdkReactNativeSpecJSI>(params);
20
+ }
21
+ #endif
22
+ // ----------------------------------------
23
+
24
+ // 1. Declare the events JavaScript is allowed to listen to
25
+ - (NSArray<NSString *> *)supportedEvents {
26
+ return @[@"onLuneEvent", @"onLuneError", @"onLuneSuccess", @"onLuneTokenRefresh"];
27
+ }
28
+
29
+ // 2. Initialize the SDK
30
+ RCT_EXPORT_METHOD(initializeSdk:(NSString *)baseUrl
31
+ accessToken:(NSString *)accessToken
32
+ resolve:(RCTPromiseResolveBlock)resolve
33
+ reject:(RCTPromiseRejectBlock)reject)
34
+ {
35
+ PfmSdkReactNativeImpl *sharedImpl = [PfmSdkReactNativeImpl shared];
36
+
37
+ sharedImpl.sendEvent = ^(NSString *eventName, id body) {
38
+ [self sendEventWithName:eventName body:body];
39
+ };
40
+
41
+ [sharedImpl initializeSdk:baseUrl accessToken:accessToken resolve:resolve reject:reject];
42
+ }
43
+
44
+ // 3. NEW: Present the Modal View
45
+ RCT_EXPORT_METHOD(showLuneView:(NSString *)viewIdentifier
46
+ resolve:(RCTPromiseResolveBlock)resolve
47
+ reject:(RCTPromiseRejectBlock)reject)
48
+ {
49
+ [[PfmSdkReactNativeImpl shared] showLuneView:viewIdentifier resolve:resolve reject:reject];
50
+ }
51
+
52
+ RCT_EXPORT_METHOD(optInForEventListener:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
53
+ {
54
+ [[PfmSdkReactNativeImpl shared] optInForEventListener:resolve reject:reject];
55
+ }
56
+
57
+ RCT_EXPORT_METHOD(optInForErrorLogger:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
58
+ {
59
+ [[PfmSdkReactNativeImpl shared] optInForErrorLogger:resolve reject:reject];
60
+ }
61
+
62
+ RCT_EXPORT_METHOD(optInForErrorNotifier:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
63
+ {
64
+ [[PfmSdkReactNativeImpl shared] optInForErrorNotifier:resolve reject:reject];
65
+ }
66
+
67
+ RCT_EXPORT_METHOD(optInForSuccessNotifier:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
68
+ {
69
+ [[PfmSdkReactNativeImpl shared] optInForSuccessNotifier:resolve reject:reject];
70
+ }
71
+
72
+ RCT_EXPORT_METHOD(registerRefreshTokenCallback:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
73
+ {
74
+ [[PfmSdkReactNativeImpl shared] registerRefreshTokenCallback:resolve reject:reject];
75
+ }
76
+
77
+ RCT_EXPORT_METHOD(destroySdk:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
78
+ {
79
+ [[PfmSdkReactNativeImpl shared] destroySdk:resolve reject:reject];
80
+ }
81
+
82
+ RCT_EXPORT_METHOD(addListener:(NSString *)eventName) {}
83
+ RCT_EXPORT_METHOD(removeListeners:(double)count) {}
84
+
85
+ + (BOOL)requiresMainQueueSetup
86
+ {
87
+ return YES;
88
+ }
89
+
90
+ @end
@@ -0,0 +1,130 @@
1
+ import Foundation
2
+ import React
3
+ import SwiftUI // <-- Required to present SwiftUI Views!
4
+ import LuneSDK
5
+
6
+ @objc(PfmSdkReactNativeImpl)
7
+ public class PfmSdkReactNativeImpl: NSObject {
8
+
9
+ @objc public static let shared = PfmSdkReactNativeImpl()
10
+
11
+ @objc public var sendEvent: ((String, Any?) -> Void)?
12
+
13
+ var sdkManager: LuneSDKManager?
14
+
15
+ // Cache these to easily pass into the SwiftUI View later
16
+ var savedBaseUrl: String = ""
17
+ var savedToken: String = ""
18
+
19
+ @objc(initializeSdk:accessToken:resolve:reject:)
20
+ public func initializeSdk(baseUrl: String, accessToken: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
21
+ // Safety check: ensure the strings aren't empty
22
+ guard !baseUrl.isEmpty, !accessToken.isEmpty else {
23
+ reject("INVALID_PARAMS", "Base URL or Access Token is empty", nil)
24
+ return
25
+ }
26
+
27
+ DispatchQueue.main.async {
28
+ self.savedBaseUrl = baseUrl
29
+ self.savedToken = accessToken
30
+
31
+ // Wrap in a do-catch or check if the LuneSDK expects a specific URL format
32
+ self.sdkManager = LuneSDKManager(baseUrl: baseUrl, token: accessToken)
33
+
34
+ print("Lune SDK Initialized with URL: \(baseUrl)")
35
+ resolve(nil)
36
+ }
37
+ }
38
+
39
+ // NEW: Cordova-Style Presentation
40
+ @objc(showLuneView:resolve:reject:)
41
+ public func showLuneView(viewIdentifier: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
42
+ DispatchQueue.main.async {
43
+ guard self.sdkManager != nil else {
44
+ reject("NOT_INITIALIZED", "Lune SDK is not initialized yet.", nil)
45
+ return
46
+ }
47
+
48
+ // 1. Create the SwiftUI View
49
+ let swiftUIView = LuneSelectorView(
50
+ viewType: viewIdentifier,
51
+ baseUrl: self.savedBaseUrl,
52
+ token: self.savedToken
53
+ )
54
+
55
+ // 2. Wrap it in a UIViewController
56
+ let hostingController = UIHostingController(rootView: swiftUIView)
57
+ hostingController.modalPresentationStyle = .pageSheet // Creates that nice slide-up modal effect
58
+
59
+ // 3. Find the currently visible View Controller
60
+ guard let windowScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene,
61
+ let rootVC = windowScene.windows.first(where: { $0.isKeyWindow })?.rootViewController else {
62
+ reject("UI_ERROR", "Could not find root view controller to present Lune View", nil)
63
+ return
64
+ }
65
+
66
+ var topVC = rootVC
67
+ while let presented = topVC.presentedViewController {
68
+ topVC = presented
69
+ }
70
+
71
+ // 4. Present it!
72
+ topVC.present(hostingController, animated: true) {
73
+ resolve(nil)
74
+ }
75
+ }
76
+ }
77
+
78
+ @objc(optInForEventListener:reject:)
79
+ public func optInForEventListener(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
80
+ guard let manager = self.sdkManager else {
81
+ reject("NOT_INITIALIZED", "Lune SDK is not initialized yet.", nil)
82
+ return
83
+ }
84
+ manager.initializeLogger { event in
85
+ self.sendEvent?("onLuneEvent", event)
86
+ }
87
+ resolve(nil)
88
+ }
89
+
90
+ @objc(optInForErrorLogger:reject:)
91
+ public func optInForErrorLogger(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
92
+ self.sdkManager?.setUpErrorLogger { error in
93
+ self.sendEvent?("onLuneError", error.localizedDescription)
94
+ }
95
+ resolve(nil)
96
+ }
97
+
98
+ @objc(optInForErrorNotifier:reject:)
99
+ public func optInForErrorNotifier(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
100
+ self.sdkManager?.setUpErrorNotifier { errorMessage in
101
+ self.sendEvent?("onLuneError", errorMessage)
102
+ }
103
+ resolve(nil)
104
+ }
105
+
106
+ @objc(optInForSuccessNotifier:reject:)
107
+ public func optInForSuccessNotifier(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
108
+ self.sdkManager?.setUpSuccessNotifier { successMessage in
109
+ self.sendEvent?("onLuneSuccess", successMessage)
110
+ }
111
+ resolve(nil)
112
+ }
113
+
114
+ @objc(registerRefreshTokenCallback:reject:)
115
+ public func registerRefreshTokenCallback(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
116
+ self.sdkManager?.setupRefreshCallback {
117
+ self.sendEvent?("onLuneTokenRefresh", nil)
118
+ return nil
119
+ }
120
+ resolve(nil)
121
+ }
122
+
123
+ @objc(destroySdk:reject:)
124
+ public func destroySdk(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
125
+ self.sdkManager = nil
126
+ self.savedBaseUrl = ""
127
+ self.savedToken = ""
128
+ resolve(nil)
129
+ }
130
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+
5
+ // This mirrors your Dart LuneViewType enum
6
+ export let LuneViewType = /*#__PURE__*/function (LuneViewType) {
7
+ LuneViewType["CATEGORY_SPEND_CHART"] = "category_spend_chart";
8
+ LuneViewType["CASHFLOW_CHART"] = "cashflow_chart";
9
+ LuneViewType["CATEGORY_TREND_CHART"] = "category_trend_chart";
10
+ LuneViewType["BRAND_TREND_CHART"] = "brand_trend_chart";
11
+ LuneViewType["BRAND_LIST"] = "brand_list";
12
+ LuneViewType["TRANSACTION_LIST"] = "transaction_list";
13
+ LuneViewType["EXPENSE_VIEW"] = "spend";
14
+ LuneViewType["CASHFLOW_VIEW"] = "cashflow";
15
+ LuneViewType["BUDGET_VIEW"] = "budget";
16
+ LuneViewType["BRAND_TRENDS_VIEW"] = "brand_trend";
17
+ LuneViewType["CATEGORY_TREND_VIEW"] = "category_trend";
18
+ return LuneViewType;
19
+ }({});
20
+
21
+ // This mirrors your Dart LunePlatform abstract class
22
+
23
+ export default TurboModuleRegistry.getEnforcing('PfmSdkReactNative');
24
+ //# sourceMappingURL=NativePfmSdkReactNative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","LuneViewType","getEnforcing"],"sourceRoot":"../../src","sources":["NativePfmSdkReactNative.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAElD;AACA,WAAYC,YAAY,0BAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA;;AAcxB;;AAkBA,eAAeD,mBAAmB,CAACE,YAAY,CAAO,mBAAmB,CAAC","ignoreList":[]}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+
3
+ import { NativeEventEmitter } from 'react-native';
4
+ import PfmSdkReactNative from "./NativePfmSdkReactNative.js";
5
+
6
+ // 1. Export the Types/Enums
7
+ export { LuneViewType } from "./NativePfmSdkReactNative.js";
8
+
9
+ // Create the emitter connected to your native module
10
+ const emitter = new NativeEventEmitter(PfmSdkReactNative);
11
+
12
+ // --- Core Methods ---
13
+ export function initializeSdk(baseUrl, accessToken) {
14
+ return PfmSdkReactNative.initializeSdk(baseUrl, accessToken);
15
+ }
16
+ export function optInForEventListener() {
17
+ return PfmSdkReactNative.optInForEventListener();
18
+ }
19
+ export function optInForErrorLogger() {
20
+ return PfmSdkReactNative.optInForErrorLogger();
21
+ }
22
+ export function optInForErrorNotifier() {
23
+ return PfmSdkReactNative.optInForErrorNotifier();
24
+ }
25
+ export function optInForSuccessNotifier() {
26
+ return PfmSdkReactNative.optInForSuccessNotifier();
27
+ }
28
+ export function registerRefreshTokenCallback() {
29
+ return PfmSdkReactNative.registerRefreshTokenCallback();
30
+ }
31
+ export function destroySdk() {
32
+ return PfmSdkReactNative.destroySdk();
33
+ }
34
+
35
+ // --- View Presentation Methods (Cordova Style) ---
36
+ export function showLuneView(viewIdentifier) {
37
+ return PfmSdkReactNative.showLuneView(viewIdentifier);
38
+ }
39
+ export function showCashflowComponent() {
40
+ return showLuneView('cashflow');
41
+ }
42
+ export function showBudgetComponent() {
43
+ return showLuneView('budget');
44
+ }
45
+ export function showExpenseComponent() {
46
+ return showLuneView('spend');
47
+ }
48
+ export function showBrandTrendComponent() {
49
+ console.warn('Brand trends not supported on native yet, showing Spend.');
50
+ return showLuneView('spend');
51
+ }
52
+
53
+ // --- Event Listeners ---
54
+ export function addLuneEventListener(callback) {
55
+ return emitter.addListener('onLuneEvent', event => callback(event));
56
+ }
57
+ export function addLuneErrorListener(callback) {
58
+ return emitter.addListener('onLuneError', error => callback(String(error)));
59
+ }
60
+ export function addLuneSuccessListener(callback) {
61
+ return emitter.addListener('onLuneSuccess', message => callback(String(message)));
62
+ }
63
+ export function addLuneTokenRefreshListener(callback) {
64
+ return emitter.addListener('onLuneTokenRefresh', () => callback());
65
+ }
66
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeEventEmitter","PfmSdkReactNative","LuneViewType","emitter","initializeSdk","baseUrl","accessToken","optInForEventListener","optInForErrorLogger","optInForErrorNotifier","optInForSuccessNotifier","registerRefreshTokenCallback","destroySdk","showLuneView","viewIdentifier","showCashflowComponent","showBudgetComponent","showExpenseComponent","showBrandTrendComponent","console","warn","addLuneEventListener","callback","addListener","event","addLuneErrorListener","error","String","addLuneSuccessListener","message","addLuneTokenRefreshListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,kBAAkB,QAAQ,cAAc;AAEjD,OAAOC,iBAAiB,MAAM,8BAA2B;;AAEzD;AACA,SAASC,YAAY,QAAQ,8BAA2B;;AAExD;AACA,MAAMC,OAAO,GAAG,IAAIH,kBAAkB,CAACC,iBAAwB,CAAC;;AAEhE;AACA,OAAO,SAASG,aAAaA,CAC3BC,OAAe,EACfC,WAAmB,EACJ;EACf,OAAOL,iBAAiB,CAACG,aAAa,CAACC,OAAO,EAAEC,WAAW,CAAC;AAC9D;AAEA,OAAO,SAASC,qBAAqBA,CAAA,EAAkB;EACrD,OAAON,iBAAiB,CAACM,qBAAqB,CAAC,CAAC;AAClD;AAEA,OAAO,SAASC,mBAAmBA,CAAA,EAAkB;EACnD,OAAOP,iBAAiB,CAACO,mBAAmB,CAAC,CAAC;AAChD;AAEA,OAAO,SAASC,qBAAqBA,CAAA,EAAkB;EACrD,OAAOR,iBAAiB,CAACQ,qBAAqB,CAAC,CAAC;AAClD;AAEA,OAAO,SAASC,uBAAuBA,CAAA,EAAkB;EACvD,OAAOT,iBAAiB,CAACS,uBAAuB,CAAC,CAAC;AACpD;AAEA,OAAO,SAASC,4BAA4BA,CAAA,EAAkB;EAC5D,OAAOV,iBAAiB,CAACU,4BAA4B,CAAC,CAAC;AACzD;AAEA,OAAO,SAASC,UAAUA,CAAA,EAAkB;EAC1C,OAAOX,iBAAiB,CAACW,UAAU,CAAC,CAAC;AACvC;;AAEA;AACA,OAAO,SAASC,YAAYA,CAACC,cAAsB,EAAiB;EAClE,OAAOb,iBAAiB,CAACY,YAAY,CAACC,cAAc,CAAC;AACvD;AAEA,OAAO,SAASC,qBAAqBA,CAAA,EAAkB;EACrD,OAAOF,YAAY,CAAC,UAAU,CAAC;AACjC;AAEA,OAAO,SAASG,mBAAmBA,CAAA,EAAkB;EACnD,OAAOH,YAAY,CAAC,QAAQ,CAAC;AAC/B;AAEA,OAAO,SAASI,oBAAoBA,CAAA,EAAkB;EACpD,OAAOJ,YAAY,CAAC,OAAO,CAAC;AAC9B;AAEA,OAAO,SAASK,uBAAuBA,CAAA,EAAkB;EACvDC,OAAO,CAACC,IAAI,CAAC,0DAA0D,CAAC;EACxE,OAAOP,YAAY,CAAC,OAAO,CAAC;AAC9B;;AAEA;AACA,OAAO,SAASQ,oBAAoBA,CAClCC,QAA8B,EACX;EACnB,OAAOnB,OAAO,CAACoB,WAAW,CAAC,aAAa,EAAGC,KAAU,IAAKF,QAAQ,CAACE,KAAK,CAAC,CAAC;AAC5E;AAEA,OAAO,SAASC,oBAAoBA,CAClCH,QAAiC,EACd;EACnB,OAAOnB,OAAO,CAACoB,WAAW,CAAC,aAAa,EAAGG,KAAU,IACnDJ,QAAQ,CAACK,MAAM,CAACD,KAAK,CAAC,CACxB,CAAC;AACH;AAEA,OAAO,SAASE,sBAAsBA,CACpCN,QAAmC,EAChB;EACnB,OAAOnB,OAAO,CAACoB,WAAW,CAAC,eAAe,EAAGM,OAAY,IACvDP,QAAQ,CAACK,MAAM,CAACE,OAAO,CAAC,CAC1B,CAAC;AACH;AAEA,OAAO,SAASC,2BAA2BA,CACzCR,QAAoB,EACD;EACnB,OAAOnB,OAAO,CAACoB,WAAW,CAAC,oBAAoB,EAAE,MAAMD,QAAQ,CAAC,CAAC,CAAC;AACpE","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,29 @@
1
+ import type { TurboModule } from 'react-native';
2
+ export declare enum LuneViewType {
3
+ CATEGORY_SPEND_CHART = "category_spend_chart",
4
+ CASHFLOW_CHART = "cashflow_chart",
5
+ CATEGORY_TREND_CHART = "category_trend_chart",
6
+ BRAND_TREND_CHART = "brand_trend_chart",
7
+ BRAND_LIST = "brand_list",
8
+ TRANSACTION_LIST = "transaction_list",
9
+ EXPENSE_VIEW = "spend",
10
+ CASHFLOW_VIEW = "cashflow",
11
+ BUDGET_VIEW = "budget",
12
+ BRAND_TRENDS_VIEW = "brand_trend",
13
+ CATEGORY_TREND_VIEW = "category_trend"
14
+ }
15
+ export interface Spec extends TurboModule {
16
+ initializeSdk(baseUrl: string, accessToken: string): Promise<void>;
17
+ optInForEventListener(): Promise<void>;
18
+ optInForErrorLogger(): Promise<void>;
19
+ optInForErrorNotifier(): Promise<void>;
20
+ optInForSuccessNotifier(): Promise<void>;
21
+ registerRefreshTokenCallback(): Promise<void>;
22
+ destroySdk(): Promise<void>;
23
+ showLuneView(viewIdentifier: string): Promise<void>;
24
+ addListener(eventName: string): void;
25
+ removeListeners(count: number): void;
26
+ }
27
+ declare const _default: Spec;
28
+ export default _default;
29
+ //# sourceMappingURL=NativePfmSdkReactNative.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativePfmSdkReactNative.d.ts","sourceRoot":"","sources":["../../../src/NativePfmSdkReactNative.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAIhD,oBAAY,YAAY;IACtB,oBAAoB,yBAAyB;IAC7C,cAAc,mBAAmB;IACjC,oBAAoB,yBAAyB;IAC7C,iBAAiB,sBAAsB;IACvC,UAAU,eAAe;IACzB,gBAAgB,qBAAqB;IACrC,YAAY,UAAU;IACtB,aAAa,aAAa;IAC1B,WAAW,WAAW;IACtB,iBAAiB,gBAAgB;IACjC,mBAAmB,mBAAmB;CACvC;AAGD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAG5B,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGpD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAA2E"}
@@ -0,0 +1,19 @@
1
+ import type { EventSubscription } from 'react-native';
2
+ export { LuneViewType } from './NativePfmSdkReactNative';
3
+ export declare function initializeSdk(baseUrl: string, accessToken: string): Promise<void>;
4
+ export declare function optInForEventListener(): Promise<void>;
5
+ export declare function optInForErrorLogger(): Promise<void>;
6
+ export declare function optInForErrorNotifier(): Promise<void>;
7
+ export declare function optInForSuccessNotifier(): Promise<void>;
8
+ export declare function registerRefreshTokenCallback(): Promise<void>;
9
+ export declare function destroySdk(): Promise<void>;
10
+ export declare function showLuneView(viewIdentifier: string): Promise<void>;
11
+ export declare function showCashflowComponent(): Promise<void>;
12
+ export declare function showBudgetComponent(): Promise<void>;
13
+ export declare function showExpenseComponent(): Promise<void>;
14
+ export declare function showBrandTrendComponent(): Promise<void>;
15
+ export declare function addLuneEventListener(callback: (event: any) => void): EventSubscription;
16
+ export declare function addLuneErrorListener(callback: (error: string) => void): EventSubscription;
17
+ export declare function addLuneSuccessListener(callback: (message: string) => void): EventSubscription;
18
+ export declare function addLuneTokenRefreshListener(callback: () => void): EventSubscription;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAItD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAMzD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAErD;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnD;AAED,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAErD;AAED,wBAAgB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEvD;AAED,wBAAgB,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAE5D;AAED,wBAAgB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C;AAGD,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAElE;AAED,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAErD;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnD;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEpD;AAED,wBAAgB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAGvD;AAGD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAC7B,iBAAiB,CAEnB;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAChC,iBAAiB,CAInB;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAClC,iBAAiB,CAInB;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,IAAI,GACnB,iBAAiB,CAEnB"}
package/package.json ADDED
@@ -0,0 +1,175 @@
1
+ {
2
+ "name": "react-native-pfm-sdk-react-native",
3
+ "version": "0.1.0",
4
+ "description": "React native plugin that intergates with native iOS and Android SDKs",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
34
+ "scripts": {
35
+ "example": "yarn workspace react-native-pfm-sdk-react-native-example",
36
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
+ "test": "jest",
41
+ "release": "release-it --only-version"
42
+ },
43
+ "keywords": [
44
+ "react-native",
45
+ "ios",
46
+ "android"
47
+ ],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/eslam-ali-ios-lune/react-native-pfm-sdk-react-native.git"
51
+ },
52
+ "author": "Eslam Ali <eslam.ali@lunedata.io> (https://github.com/eslam-ali-ios-lune)",
53
+ "license": "MIT",
54
+ "bugs": {
55
+ "url": "https://github.com/eslam-ali-ios-lune/react-native-pfm-sdk-react-native/issues"
56
+ },
57
+ "homepage": "https://github.com/eslam-ali-ios-lune/react-native-pfm-sdk-react-native#readme",
58
+ "publishConfig": {
59
+ "registry": "https://registry.npmjs.org/"
60
+ },
61
+ "devDependencies": {
62
+ "@commitlint/config-conventional": "^19.8.1",
63
+ "@eslint/compat": "^1.3.2",
64
+ "@eslint/eslintrc": "^3.3.1",
65
+ "@eslint/js": "^9.35.0",
66
+ "@react-native/babel-preset": "0.83.0",
67
+ "@react-native/eslint-config": "0.83.0",
68
+ "@release-it/conventional-changelog": "^10.0.1",
69
+ "@types/jest": "^29.5.14",
70
+ "@types/react": "^19.2.0",
71
+ "commitlint": "^19.8.1",
72
+ "del-cli": "^6.0.0",
73
+ "eslint": "^9.35.0",
74
+ "eslint-config-prettier": "^10.1.8",
75
+ "eslint-plugin-prettier": "^5.5.4",
76
+ "jest": "^29.7.0",
77
+ "lefthook": "^2.0.3",
78
+ "prettier": "^2.8.8",
79
+ "react": "19.2.0",
80
+ "react-native": "0.83.0",
81
+ "react-native-builder-bob": "^0.40.13",
82
+ "release-it": "^19.0.4",
83
+ "turbo": "^2.5.6",
84
+ "typescript": "^5.9.2"
85
+ },
86
+ "peerDependencies": {
87
+ "react": "*",
88
+ "react-native": "*"
89
+ },
90
+ "workspaces": {
91
+ "packages": [
92
+ "example"
93
+ ],
94
+ "nohoist": [
95
+ "**/@react-native-async-storage/async-storage",
96
+ "**/@react-native-async-storage/async-storage/**"
97
+ ]
98
+ },
99
+ "packageManager": "yarn@4.11.0",
100
+ "react-native-builder-bob": {
101
+ "source": "src",
102
+ "output": "lib",
103
+ "targets": [
104
+ [
105
+ "module",
106
+ {
107
+ "esm": true
108
+ }
109
+ ],
110
+ [
111
+ "typescript",
112
+ {
113
+ "project": "tsconfig.build.json"
114
+ }
115
+ ]
116
+ ]
117
+ },
118
+ "codegenConfig": {
119
+ "name": "RNPfmSdkReactNativeSpec",
120
+ "type": "modules",
121
+ "jsSrcsDir": "src",
122
+ "android": {
123
+ "javaPackageName": "com.pfmsdkreactnative"
124
+ }
125
+ },
126
+ "prettier": {
127
+ "quoteProps": "consistent",
128
+ "singleQuote": true,
129
+ "tabWidth": 2,
130
+ "trailingComma": "es5",
131
+ "useTabs": false
132
+ },
133
+ "jest": {
134
+ "preset": "react-native",
135
+ "modulePathIgnorePatterns": [
136
+ "<rootDir>/example/node_modules",
137
+ "<rootDir>/lib/"
138
+ ]
139
+ },
140
+ "commitlint": {
141
+ "extends": [
142
+ "@commitlint/config-conventional"
143
+ ]
144
+ },
145
+ "release-it": {
146
+ "git": {
147
+ "commitMessage": "chore: release ${version}",
148
+ "tagName": "v${version}"
149
+ },
150
+ "npm": {
151
+ "publish": true
152
+ },
153
+ "github": {
154
+ "release": true
155
+ },
156
+ "plugins": {
157
+ "@release-it/conventional-changelog": {
158
+ "preset": {
159
+ "name": "angular"
160
+ }
161
+ }
162
+ }
163
+ },
164
+ "create-react-native-library": {
165
+ "type": "turbo-module",
166
+ "languages": "kotlin-objc",
167
+ "tools": [
168
+ "eslint",
169
+ "jest",
170
+ "lefthook",
171
+ "release-it"
172
+ ],
173
+ "version": "0.57.2"
174
+ }
175
+ }
@@ -0,0 +1,37 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+
4
+ // This mirrors your Dart LuneViewType enum
5
+ export enum LuneViewType {
6
+ CATEGORY_SPEND_CHART = 'category_spend_chart',
7
+ CASHFLOW_CHART = 'cashflow_chart',
8
+ CATEGORY_TREND_CHART = 'category_trend_chart',
9
+ BRAND_TREND_CHART = 'brand_trend_chart',
10
+ BRAND_LIST = 'brand_list',
11
+ TRANSACTION_LIST = 'transaction_list',
12
+ EXPENSE_VIEW = 'spend',
13
+ CASHFLOW_VIEW = 'cashflow',
14
+ BUDGET_VIEW = 'budget',
15
+ BRAND_TRENDS_VIEW = 'brand_trend',
16
+ CATEGORY_TREND_VIEW = 'category_trend',
17
+ }
18
+
19
+ // This mirrors your Dart LunePlatform abstract class
20
+ export interface Spec extends TurboModule {
21
+ initializeSdk(baseUrl: string, accessToken: string): Promise<void>;
22
+ optInForEventListener(): Promise<void>;
23
+ optInForErrorLogger(): Promise<void>;
24
+ optInForErrorNotifier(): Promise<void>;
25
+ optInForSuccessNotifier(): Promise<void>;
26
+ registerRefreshTokenCallback(): Promise<void>;
27
+ destroySdk(): Promise<void>;
28
+
29
+ // 🚀 NEW METHOD ADDED HERE:
30
+ showLuneView(viewIdentifier: string): Promise<void>;
31
+
32
+ // REQUIRED FOR EVENT EMITTERS:
33
+ addListener(eventName: string): void;
34
+ removeListeners(count: number): void;
35
+ }
36
+
37
+ export default TurboModuleRegistry.getEnforcing<Spec>('PfmSdkReactNative');
package/src/index.tsx ADDED
@@ -0,0 +1,92 @@
1
+ import { NativeEventEmitter } from 'react-native';
2
+ import type { EventSubscription } from 'react-native';
3
+ import PfmSdkReactNative from './NativePfmSdkReactNative';
4
+
5
+ // 1. Export the Types/Enums
6
+ export { LuneViewType } from './NativePfmSdkReactNative';
7
+
8
+ // Create the emitter connected to your native module
9
+ const emitter = new NativeEventEmitter(PfmSdkReactNative as any);
10
+
11
+ // --- Core Methods ---
12
+ export function initializeSdk(
13
+ baseUrl: string,
14
+ accessToken: string
15
+ ): Promise<void> {
16
+ return PfmSdkReactNative.initializeSdk(baseUrl, accessToken);
17
+ }
18
+
19
+ export function optInForEventListener(): Promise<void> {
20
+ return PfmSdkReactNative.optInForEventListener();
21
+ }
22
+
23
+ export function optInForErrorLogger(): Promise<void> {
24
+ return PfmSdkReactNative.optInForErrorLogger();
25
+ }
26
+
27
+ export function optInForErrorNotifier(): Promise<void> {
28
+ return PfmSdkReactNative.optInForErrorNotifier();
29
+ }
30
+
31
+ export function optInForSuccessNotifier(): Promise<void> {
32
+ return PfmSdkReactNative.optInForSuccessNotifier();
33
+ }
34
+
35
+ export function registerRefreshTokenCallback(): Promise<void> {
36
+ return PfmSdkReactNative.registerRefreshTokenCallback();
37
+ }
38
+
39
+ export function destroySdk(): Promise<void> {
40
+ return PfmSdkReactNative.destroySdk();
41
+ }
42
+
43
+ // --- View Presentation Methods (Cordova Style) ---
44
+ export function showLuneView(viewIdentifier: string): Promise<void> {
45
+ return PfmSdkReactNative.showLuneView(viewIdentifier);
46
+ }
47
+
48
+ export function showCashflowComponent(): Promise<void> {
49
+ return showLuneView('cashflow');
50
+ }
51
+
52
+ export function showBudgetComponent(): Promise<void> {
53
+ return showLuneView('budget');
54
+ }
55
+
56
+ export function showExpenseComponent(): Promise<void> {
57
+ return showLuneView('spend');
58
+ }
59
+
60
+ export function showBrandTrendComponent(): Promise<void> {
61
+ console.warn('Brand trends not supported on native yet, showing Spend.');
62
+ return showLuneView('spend');
63
+ }
64
+
65
+ // --- Event Listeners ---
66
+ export function addLuneEventListener(
67
+ callback: (event: any) => void
68
+ ): EventSubscription {
69
+ return emitter.addListener('onLuneEvent', (event: any) => callback(event));
70
+ }
71
+
72
+ export function addLuneErrorListener(
73
+ callback: (error: string) => void
74
+ ): EventSubscription {
75
+ return emitter.addListener('onLuneError', (error: any) =>
76
+ callback(String(error))
77
+ );
78
+ }
79
+
80
+ export function addLuneSuccessListener(
81
+ callback: (message: string) => void
82
+ ): EventSubscription {
83
+ return emitter.addListener('onLuneSuccess', (message: any) =>
84
+ callback(String(message))
85
+ );
86
+ }
87
+
88
+ export function addLuneTokenRefreshListener(
89
+ callback: () => void
90
+ ): EventSubscription {
91
+ return emitter.addListener('onLuneTokenRefresh', () => callback());
92
+ }