@1selfworld/adchain-sdk-react-native 1.0.1

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.
@@ -0,0 +1,14 @@
1
+ package com.adchain.exposdk
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class AdchainSdkPackage : ReactPackage {
9
+ override fun createNativeModules(rc: ReactApplicationContext): List<NativeModule> =
10
+ listOf(AdchainSdkModule(rc))
11
+
12
+ override fun createViewManagers(rc: ReactApplicationContext): List<ViewManager<*, *>> =
13
+ listOf(AdchainOfferwallViewManager())
14
+ }
package/app.plugin.js ADDED
@@ -0,0 +1,17 @@
1
+ const withAdchainConfig = require('./plugin/src/withAdchainConfig');
2
+ const withAdchainAndroid = require('./plugin/src/withAdchainAndroid');
3
+ const withAdchainIOS = require('./plugin/src/withAdchainIOS');
4
+
5
+ /**
6
+ * v4.1 AdChain Expo SDK Plugin
7
+ * - Config: app.json extra 주입
8
+ * - Android: settings.gradle에 JitPack 추가
9
+ * - iOS: Podfile에 Git Pod 추가 (Swift 5.5)
10
+ */
11
+ module.exports = (config, props = {}) => {
12
+ config = withAdchainConfig(config, props);
13
+ config = withAdchainAndroid(config);
14
+ config = withAdchainIOS(config);
15
+
16
+ return config;
17
+ };
@@ -0,0 +1,17 @@
1
+ #import <React/RCTViewManager.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface RCT_EXTERN_MODULE(AdchainOfferwallViewManager, RCTViewManager)
5
+
6
+ RCT_EXPORT_VIEW_PROPERTY(placementId, NSString)
7
+ RCT_EXPORT_VIEW_PROPERTY(onOfferwallOpened, RCTDirectEventBlock)
8
+ RCT_EXPORT_VIEW_PROPERTY(onOfferwallClosed, RCTDirectEventBlock)
9
+ RCT_EXPORT_VIEW_PROPERTY(onOfferwallError, RCTDirectEventBlock)
10
+ RCT_EXPORT_VIEW_PROPERTY(onRewardEarned, RCTDirectEventBlock)
11
+ RCT_EXPORT_VIEW_PROPERTY(onCustomEvent, RCTDirectEventBlock)
12
+ RCT_EXPORT_VIEW_PROPERTY(onDataRequest, RCTDirectEventBlock)
13
+
14
+ // Export sendDataResponse method for handling data request responses
15
+ RCT_EXTERN_METHOD(sendDataResponse:(nonnull NSNumber *)reactTag requestId:(NSString *)requestId responseData:(NSDictionary *)responseData)
16
+
17
+ @end
@@ -0,0 +1,242 @@
1
+ import Foundation
2
+ import React
3
+ import AdchainSDK
4
+
5
+ @objc(AdchainOfferwallViewManager)
6
+ class AdchainOfferwallViewManager: RCTViewManager {
7
+
8
+ override func view() -> UIView! {
9
+ let offerwallView = AdchainOfferwallRNView()
10
+ return offerwallView
11
+ }
12
+
13
+ override static func requiresMainQueueSetup() -> Bool {
14
+ return true
15
+ }
16
+
17
+ // NEW: Handle sendDataResponse command from React Native
18
+ @objc func sendDataResponse(_ reactTag: NSNumber, requestId: String, responseData: NSDictionary) {
19
+ DispatchQueue.main.async { [weak self] in
20
+ self?.bridge.uiManager.addUIBlock { (uiManager, viewRegistry) in
21
+ guard let view = viewRegistry?[reactTag] as? AdchainOfferwallRNView else {
22
+ return
23
+ }
24
+ view.sendDataResponse(requestId, responseData: responseData as! [String: Any])
25
+ }
26
+ }
27
+ }
28
+ }
29
+
30
+ // Wrapper view for React Native
31
+ @objc(AdchainOfferwallRNView)
32
+ class AdchainOfferwallRNView: UIView {
33
+
34
+ private var offerwallView: AdchainOfferwallView?
35
+ private var pendingPlacementId: String?
36
+
37
+ @objc var placementId: NSString = "" {
38
+ didSet {
39
+ let placementIdStr = placementId as String
40
+ guard !placementIdStr.isEmpty else { return }
41
+
42
+ pendingPlacementId = placementIdStr
43
+ loadOfferwallIfReady()
44
+ }
45
+ }
46
+
47
+ @objc var onOfferwallOpened: RCTDirectEventBlock?
48
+ @objc var onOfferwallClosed: RCTDirectEventBlock?
49
+ @objc var onOfferwallError: RCTDirectEventBlock?
50
+ @objc var onRewardEarned: RCTDirectEventBlock?
51
+ @objc var onCustomEvent: RCTDirectEventBlock? // NEW
52
+ @objc var onDataRequest: RCTDirectEventBlock? // NEW
53
+
54
+ // Store pending data responses
55
+ private var pendingDataResponses: [String: [String: Any]] = [:]
56
+
57
+ override init(frame: CGRect) {
58
+ super.init(frame: frame)
59
+ setupOfferwallView()
60
+ }
61
+
62
+ required init?(coder: NSCoder) {
63
+ super.init(coder: coder)
64
+ setupOfferwallView()
65
+ }
66
+
67
+ private func setupOfferwallView() {
68
+ let view = AdchainOfferwallView(frame: bounds)
69
+ view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
70
+ addSubview(view)
71
+ offerwallView = view
72
+ }
73
+
74
+ private func loadOfferwallIfReady() {
75
+ guard let placementId = pendingPlacementId,
76
+ let view = offerwallView else {
77
+ return
78
+ }
79
+
80
+ guard let config = AdchainSdk.shared.getConfig(),
81
+ let user = AdchainSdk.shared.getCurrentUser(),
82
+ let offerwallUrl = AdchainSdk.shared.getOfferwallUrl() else {
83
+ if AdchainSdk.shared.getConfig() == nil {
84
+ sendErrorEvent(error: "SDK not initialized")
85
+ } else if AdchainSdk.shared.getCurrentUser() == nil {
86
+ sendErrorEvent(error: "User not logged in")
87
+ } else {
88
+ sendErrorEvent(error: "Offerwall URL not available")
89
+ }
90
+ return
91
+ }
92
+
93
+ // Set callback for offerwall events
94
+ view.setCallback(RNOfferwallCallback(
95
+ onOpened: { [weak self] in
96
+ self?.sendEvent(name: "onOfferwallOpened", body: [:])
97
+ },
98
+ onClosed: { [weak self] in
99
+ self?.sendEvent(name: "onOfferwallClosed", body: [:])
100
+ },
101
+ onError: { [weak self] error in
102
+ self?.sendEvent(name: "onOfferwallError", body: ["error": error])
103
+ },
104
+ onRewardEarned: { [weak self] amount in
105
+ self?.sendEvent(name: "onRewardEarned", body: ["amount": amount])
106
+ }
107
+ ))
108
+
109
+ // NEW: Set event callback for custom events
110
+ view.setEventCallback(RNEventCallback(
111
+ onCustomEvent: { [weak self] (eventType, payload) in
112
+ self?.sendEvent(name: "onCustomEvent", body: [
113
+ "eventType": eventType,
114
+ "payload": payload
115
+ ])
116
+ },
117
+ onDataRequest: { [weak self] (requestId, requestType, params) -> [String: Any]? in
118
+ guard let self = self else { return nil }
119
+
120
+ // Send event to React Native
121
+ self.sendEvent(name: "onDataRequest", body: [
122
+ "requestId": requestId,
123
+ "requestType": requestType,
124
+ "params": params
125
+ ])
126
+
127
+ // Wait for response from React Native (with timeout)
128
+ let startTime = Date()
129
+ let timeout: TimeInterval = 5.0 // 5 seconds
130
+
131
+ while Date().timeIntervalSince(startTime) < timeout {
132
+ if let response = self.pendingDataResponses.removeValue(forKey: requestId) {
133
+ return response
134
+ }
135
+ Thread.sleep(forTimeInterval: 0.01)
136
+ }
137
+
138
+ return nil
139
+ }
140
+ ))
141
+
142
+ // Load offerwall
143
+ view.loadOfferwall(
144
+ baseUrl: offerwallUrl,
145
+ userId: user.userId,
146
+ appKey: config.appKey,
147
+ placementId: placementId
148
+ )
149
+
150
+ // Clear pending
151
+ pendingPlacementId = nil
152
+ }
153
+
154
+ private func sendEvent(name: String, body: [String: Any]) {
155
+ switch name {
156
+ case "onOfferwallOpened":
157
+ onOfferwallOpened?(body)
158
+ case "onOfferwallClosed":
159
+ onOfferwallClosed?(body)
160
+ case "onOfferwallError":
161
+ onOfferwallError?(body)
162
+ case "onRewardEarned":
163
+ onRewardEarned?(body)
164
+ case "onCustomEvent": // NEW
165
+ onCustomEvent?(body)
166
+ case "onDataRequest": // NEW
167
+ onDataRequest?(body)
168
+ default:
169
+ break
170
+ }
171
+ }
172
+
173
+ private func sendErrorEvent(error: String) {
174
+ sendEvent(name: "onOfferwallError", body: ["error": error])
175
+ }
176
+
177
+ // NEW: Handle data response from React Native
178
+ @objc func sendDataResponse(_ requestId: String, responseData: [String: Any]) {
179
+ pendingDataResponses[requestId] = responseData
180
+ }
181
+
182
+ override func layoutSubviews() {
183
+ super.layoutSubviews()
184
+ offerwallView?.frame = bounds
185
+ }
186
+ }
187
+
188
+ // Custom callback implementation for React Native
189
+ class RNOfferwallCallback: OfferwallCallback {
190
+ private let onOpenedHandler: () -> Void
191
+ private let onClosedHandler: () -> Void
192
+ private let onErrorHandler: (String) -> Void
193
+ private let onRewardEarnedHandler: (Int) -> Void
194
+
195
+ init(onOpened: @escaping () -> Void,
196
+ onClosed: @escaping () -> Void,
197
+ onError: @escaping (String) -> Void,
198
+ onRewardEarned: @escaping (Int) -> Void) {
199
+ self.onOpenedHandler = onOpened
200
+ self.onClosedHandler = onClosed
201
+ self.onErrorHandler = onError
202
+ self.onRewardEarnedHandler = onRewardEarned
203
+ }
204
+
205
+ func onOpened() {
206
+ onOpenedHandler()
207
+ }
208
+
209
+ func onClosed() {
210
+ onClosedHandler()
211
+ }
212
+
213
+ func onError(_ message: String) {
214
+ onErrorHandler(message)
215
+ }
216
+
217
+ func onRewardEarned(_ amount: Int) {
218
+ onRewardEarnedHandler(amount)
219
+ }
220
+ }
221
+
222
+ // NEW: Custom event callback implementation for React Native
223
+ class RNEventCallback: OfferwallEventCallback {
224
+ private let onCustomEventHandler: (String, [String: Any]) -> Void
225
+ private let onDataRequestHandler: (String, String, [String: Any]) -> [String: Any]?
226
+
227
+ init(
228
+ onCustomEvent: @escaping (String, [String: Any]) -> Void,
229
+ onDataRequest: @escaping (String, String, [String: Any]) -> [String: Any]?
230
+ ) {
231
+ self.onCustomEventHandler = onCustomEvent
232
+ self.onDataRequestHandler = onDataRequest
233
+ }
234
+
235
+ func onCustomEvent(eventType: String, payload: [String: Any]) {
236
+ onCustomEventHandler(eventType, payload)
237
+ }
238
+
239
+ func onDataRequest(requestId: String, requestType: String, params: [String: Any]) -> [String: Any]? {
240
+ return onDataRequestHandler(requestId, requestType, params)
241
+ }
242
+ }
@@ -0,0 +1,87 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface RCT_EXTERN_MODULE(AdchainSdk, RCTEventEmitter)
5
+
6
+ // 1. SDK 초기화
7
+ RCT_EXTERN_METHOD(initialize:(NSString *)appKey
8
+ appSecret:(NSString *)appSecret
9
+ options:(NSDictionary *)options
10
+ resolver:(RCTPromiseResolveBlock)resolver
11
+ rejecter:(RCTPromiseRejectBlock)rejecter)
12
+
13
+ // 2. 인증 관련 (4개)
14
+ RCT_EXTERN_METHOD(login:(NSString *)userId
15
+ userInfo:(NSDictionary *)userInfo
16
+ resolver:(RCTPromiseResolveBlock)resolver
17
+ rejecter:(RCTPromiseRejectBlock)rejecter)
18
+
19
+ RCT_EXTERN_METHOD(logout:(RCTPromiseResolveBlock)resolver
20
+ rejecter:(RCTPromiseRejectBlock)rejecter)
21
+
22
+ RCT_EXTERN_METHOD(isLoggedIn:(RCTPromiseResolveBlock)resolver
23
+ rejecter:(RCTPromiseRejectBlock)rejecter)
24
+
25
+ RCT_EXTERN_METHOD(getCurrentUser:(RCTPromiseResolveBlock)resolver
26
+ rejecter:(RCTPromiseRejectBlock)rejecter)
27
+
28
+ // 3. Quiz 관련 (2개)
29
+ RCT_EXTERN_METHOD(loadQuizList:(NSString *)unitId
30
+ resolver:(RCTPromiseResolveBlock)resolver
31
+ rejecter:(RCTPromiseRejectBlock)rejecter)
32
+
33
+ RCT_EXTERN_METHOD(clickQuiz:(NSString *)unitId
34
+ quizId:(NSString *)quizId
35
+ resolver:(RCTPromiseResolveBlock)resolver
36
+ rejecter:(RCTPromiseRejectBlock)rejecter)
37
+
38
+ // 4. Mission 관련 (3개)
39
+ RCT_EXTERN_METHOD(loadMissionList:(NSString *)unitId
40
+ resolver:(RCTPromiseResolveBlock)resolver
41
+ rejecter:(RCTPromiseRejectBlock)rejecter)
42
+
43
+ RCT_EXTERN_METHOD(clickMission:(NSString *)unitId
44
+ missionId:(NSString *)missionId
45
+ resolver:(RCTPromiseResolveBlock)resolver
46
+ rejecter:(RCTPromiseRejectBlock)rejecter)
47
+
48
+ RCT_EXTERN_METHOD(claimReward:(NSString *)unitId
49
+ resolver:(RCTPromiseResolveBlock)resolver
50
+ rejecter:(RCTPromiseRejectBlock)rejecter)
51
+
52
+ // 5. Debug/Utility Methods (3개)
53
+ RCT_EXTERN_METHOD(isInitialized:(RCTPromiseResolveBlock)resolver
54
+ rejecter:(RCTPromiseRejectBlock)rejecter)
55
+
56
+ RCT_EXTERN_METHOD(getUserId:(RCTPromiseResolveBlock)resolver
57
+ rejecter:(RCTPromiseRejectBlock)rejecter)
58
+
59
+ RCT_EXTERN_METHOD(getIFA:(RCTPromiseResolveBlock)resolver
60
+ rejecter:(RCTPromiseRejectBlock)rejecter)
61
+
62
+ // 6. Banner (1개)
63
+ RCT_EXTERN_METHOD(getBannerInfo:(NSString *)placementId
64
+ resolver:(RCTPromiseResolveBlock)resolver
65
+ rejecter:(RCTPromiseRejectBlock)rejecter)
66
+
67
+ // 7. Offerwall (3개)
68
+ RCT_EXTERN_METHOD(openOfferwall:(NSString * _Nullable)placementId
69
+ resolver:(RCTPromiseResolveBlock)resolver
70
+ rejecter:(RCTPromiseRejectBlock)rejecter)
71
+
72
+ RCT_EXTERN_METHOD(openOfferwallWithUrl:(NSString *)url
73
+ placementId:(NSString * _Nullable)placementId
74
+ resolver:(RCTPromiseResolveBlock)resolver
75
+ rejecter:(RCTPromiseRejectBlock)rejecter)
76
+
77
+ RCT_EXTERN_METHOD(openExternalBrowser:(NSString *)url
78
+ placementId:(NSString * _Nullable)placementId
79
+ resolver:(RCTPromiseResolveBlock)resolver
80
+ rejecter:(RCTPromiseRejectBlock)rejecter)
81
+
82
+ // 8. Adjoe (1개)
83
+ RCT_EXTERN_METHOD(openAdjoeOfferwall:(NSString * _Nullable)placementId
84
+ resolver:(RCTPromiseResolveBlock)resolver
85
+ rejecter:(RCTPromiseRejectBlock)rejecter)
86
+
87
+ @end