@1selfworld/adchain-sdk-react-native 1.0.16 → 1.0.18

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 CHANGED
@@ -99,11 +99,16 @@ export default function App() {
99
99
  ### 임베디드 오퍼월 컴포넌트
100
100
  `AdchainOfferwallView`는 탭이나 화면에 직접 임베드할 수 있는 React Native 컴포넌트입니다.
101
101
 
102
+ **주요 특징**:
103
+ - ✅ `placementId`만으로 서버에서 오퍼월 URL을 자동으로 가져옴
104
+ - ✅ `openOfferwallNestAds`와 동일한 URL 로딩 방식 사용
105
+ - ✅ 각 placement마다 다른 오퍼월 페이지 로드 가능
106
+
102
107
  ```typescript
103
108
  import { AdchainOfferwallView } from '@1selfworld/adchain-sdk-react-native';
104
109
 
105
110
  <AdchainOfferwallView
106
- placementId="tab_embedded_offerwall"
111
+ placementId="tab_embedded_offerwall" // 서버에서 URL 자동 조회
107
112
  style={{ flex: 1 }}
108
113
  onOfferwallOpened={() => console.log('Opened')}
109
114
  onOfferwallClosed={() => console.log('Closed')}
@@ -117,6 +122,11 @@ import { AdchainOfferwallView } from '@1selfworld/adchain-sdk-react-native';
117
122
  />
118
123
  ```
119
124
 
125
+ **동작 방식**:
126
+ 1. `placementId`를 입력하면 SDK가 자동으로 서버 API 호출 (`GET /v1/api/sdk/placement-url`)
127
+ 2. 서버에서 해당 placement에 매핑된 오퍼월 URL 반환
128
+ 3. 반환된 URL로 WebView 로드
129
+
120
130
  **백버튼 이벤트 (Android, v1.0.15+)**:
121
131
  - `onBackPressOnFirstPage`: WebView 스택에 페이지가 1개만 있을 때 (더 이상 뒤로갈 수 없음)
122
132
  - `onBackNavigated`: WebView 스택에서 성공적으로 뒤로 이동했을 때
@@ -214,6 +224,7 @@ npx expo run:ios
214
224
 
215
225
  | 패키지 버전 | Android SDK | iOS SDK | Swift | Expo SDK | 주요 변경사항 |
216
226
  |------------|-------------|---------|-------|----------|--------------|
227
+ | 1.0.18 | v1.0.30 | v1.0.46 | 5.5 | 50-53 | 네이티브 SDK 버전 업데이트 |
217
228
  | 1.0.15 | v1.0.29 | v1.0.45 | 5.5 | 50-53 | 백버튼 이벤트, NestAds 지원 |
218
229
  | 1.0.13 | v1.0.28 | v1.0.45 | 5.5 | 50-53 | NestAds 오퍼월 추가 |
219
230
  | 1.0.0 | v1.0.28 | v1.0.45 | 5.5 | 50-53 | 초기 릴리즈 |
@@ -35,5 +35,5 @@ dependencies {
35
35
  implementation 'com.facebook.react:react-native:+'
36
36
  implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.21"
37
37
  // AdChain Android SDK via JitPack
38
- implementation 'com.github.1selfworld-labs:adchain-sdk-android:v1.0.29'
38
+ implementation 'com.github.1selfworld-labs:adchain-sdk-android:v1.0.30'
39
39
  }
@@ -12,6 +12,11 @@ import com.adchain.sdk.core.AdchainSdk
12
12
  import com.adchain.sdk.offerwall.AdchainOfferwallView
13
13
  import com.adchain.sdk.offerwall.OfferwallCallback
14
14
  import com.adchain.sdk.offerwall.OfferwallEventCallback
15
+ import com.adchain.sdk.network.NetworkManager
16
+ import kotlinx.coroutines.CoroutineScope
17
+ import kotlinx.coroutines.Dispatchers
18
+ import kotlinx.coroutines.launch
19
+ import kotlinx.coroutines.withContext
15
20
 
16
21
  class AdchainOfferwallViewManager : SimpleViewManager<AdchainOfferwallView>() {
17
22
 
@@ -23,6 +28,9 @@ class AdchainOfferwallViewManager : SimpleViewManager<AdchainOfferwallView>() {
23
28
  private const val COMMAND_SEND_DATA_RESPONSE = 3 // NEW
24
29
  }
25
30
 
31
+ // Coroutine scope for async operations
32
+ private val coroutineScope = CoroutineScope(Dispatchers.Main)
33
+
26
34
  override fun getName() = REACT_CLASS
27
35
 
28
36
  override fun createViewInstance(reactContext: ThemedReactContext): AdchainOfferwallView {
@@ -93,12 +101,11 @@ class AdchainOfferwallViewManager : SimpleViewManager<AdchainOfferwallView>() {
93
101
  }
94
102
 
95
103
  try {
96
- // Get SDK config, user, and offerwall URL
104
+ // Get SDK config and user
97
105
  val config = AdchainSdk.getConfig()
98
106
  val user = AdchainSdk.getCurrentUser()
99
- val offerwallUrl = AdchainSdk.getOfferwallUrl()
100
107
 
101
- Log.d(TAG, "SDK state - config: ${config != null}, user: ${user != null}, url: $offerwallUrl")
108
+ Log.d(TAG, "SDK state - config: ${config != null}, user: ${user != null}")
102
109
 
103
110
  if (config == null) {
104
111
  Log.e(TAG, "SDK not initialized")
@@ -112,105 +119,117 @@ class AdchainOfferwallViewManager : SimpleViewManager<AdchainOfferwallView>() {
112
119
  return
113
120
  }
114
121
 
115
- if (offerwallUrl.isNullOrEmpty()) {
116
- Log.e(TAG, "Offerwall URL not available")
117
- sendErrorEvent(view, "Offerwall URL not available")
118
- return
119
- }
120
-
121
- Log.d(TAG, "About to call view.loadOfferwall()")
122
- Log.d(TAG, "Parameters - baseUrl: $offerwallUrl, userId: ${user.userId}, appKey: ${config.appKey}, placementId: $placementId")
123
-
124
- // Set callback for offerwall events
125
- Log.d(TAG, "Setting callback...")
126
- view.setCallback(object : OfferwallCallback {
127
- override fun onOpened() {
128
- sendEvent(view, "onOfferwallOpened", Arguments.createMap())
122
+ // Fetch placement URL from server (like openOfferwallNestAds does)
123
+ Log.d(TAG, "Fetching placement URL from server for placementId: $placementId")
124
+ coroutineScope.launch {
125
+ val result = withContext(Dispatchers.IO) {
126
+ NetworkManager.getPlacementUrl(placementId)
129
127
  }
130
128
 
131
- override fun onClosed() {
132
- sendEvent(view, "onOfferwallClosed", Arguments.createMap())
133
- }
129
+ if (result.isSuccess) {
130
+ val baseUrl = result.getOrNull()!!
131
+ Log.d(TAG, "Successfully fetched placement URL: $baseUrl")
132
+ Log.d(TAG, "About to call view.loadOfferwall()")
133
+ Log.d(TAG, "Parameters - baseUrl: $baseUrl, userId: ${user.userId}, appKey: ${config.appKey}, placementId: $placementId")
134
+
135
+ // Set callback for offerwall events
136
+ Log.d(TAG, "Setting callback...")
137
+ view.setCallback(object : OfferwallCallback {
138
+ override fun onOpened() {
139
+ sendEvent(view, "onOfferwallOpened", Arguments.createMap())
140
+ }
134
141
 
135
- override fun onError(message: String) {
136
- val map = Arguments.createMap().apply {
137
- putString("error", message)
138
- }
139
- sendEvent(view, "onOfferwallError", map)
140
- }
142
+ override fun onClosed() {
143
+ sendEvent(view, "onOfferwallClosed", Arguments.createMap())
144
+ }
141
145
 
142
- override fun onRewardEarned(amount: Int) {
143
- val map = Arguments.createMap().apply {
144
- putInt("amount", amount)
145
- }
146
- sendEvent(view, "onRewardEarned", map)
147
- }
146
+ override fun onError(message: String) {
147
+ val map = Arguments.createMap().apply {
148
+ putString("error", message)
149
+ }
150
+ sendEvent(view, "onOfferwallError", map)
151
+ }
148
152
 
149
- override fun onHeightChanged(height: Int) {
150
- Log.d(TAG, "onHeightChanged callback received - height: $height")
151
- val map = Arguments.createMap().apply {
152
- putInt("height", height)
153
- }
154
- sendEvent(view, "onHeightChange", map)
155
- }
156
- })
157
-
158
- // NEW: Set event callback for custom events
159
- Log.d(TAG, "Setting event callback...")
160
- view.setEventCallback(object : OfferwallEventCallback {
161
- override fun onCustomEvent(eventType: String, payload: Map<String, Any?>) {
162
- Log.d(TAG, "onCustomEvent received - type: $eventType, payload: $payload")
163
- // Sample 앱에서 처리하도록 이벤트만 전달 (Toast 표시 안 함)
164
-
165
- val map = Arguments.createMap().apply {
166
- putString("eventType", eventType)
167
- putMap("payload", convertMapToWritableMap(payload))
168
- }
169
- sendEvent(view, "onCustomEvent", map)
170
- }
153
+ override fun onRewardEarned(amount: Int) {
154
+ val map = Arguments.createMap().apply {
155
+ putInt("amount", amount)
156
+ }
157
+ sendEvent(view, "onRewardEarned", map)
158
+ }
171
159
 
172
- override fun onDataRequest(
173
- requestId: String,
174
- requestType: String,
175
- params: Map<String, Any?>
176
- ): Map<String, Any?>? {
177
- Log.d(TAG, "onDataRequest received - id: $requestId, type: $requestType, params: $params")
178
-
179
- // Send event to React Native
180
- val map = Arguments.createMap().apply {
181
- putString("requestId", requestId)
182
- putString("requestType", requestType)
183
- putMap("params", convertMapToWritableMap(params))
184
- }
185
- sendEvent(view, "onDataRequest", map)
186
-
187
- // Wait for response from React Native (with timeout)
188
- val startTime = System.currentTimeMillis()
189
- val timeout = 5000L // 5 seconds
190
-
191
- while (System.currentTimeMillis() - startTime < timeout) {
192
- val response = pendingDataResponses.remove(requestId)
193
- if (response != null) {
194
- Log.d(TAG, "Data response received for request: $requestId")
195
- return response
160
+ override fun onHeightChanged(height: Int) {
161
+ Log.d(TAG, "onHeightChanged callback received - height: $height")
162
+ val map = Arguments.createMap().apply {
163
+ putInt("height", height)
164
+ }
165
+ sendEvent(view, "onHeightChange", map)
166
+ }
167
+ })
168
+
169
+ // NEW: Set event callback for custom events
170
+ Log.d(TAG, "Setting event callback...")
171
+ view.setEventCallback(object : OfferwallEventCallback {
172
+ override fun onCustomEvent(eventType: String, payload: Map<String, Any?>) {
173
+ Log.d(TAG, "onCustomEvent received - type: $eventType, payload: $payload")
174
+ // Sample 앱에서 처리하도록 이벤트만 전달 (Toast 표시 안 함)
175
+
176
+ val map = Arguments.createMap().apply {
177
+ putString("eventType", eventType)
178
+ putMap("payload", convertMapToWritableMap(payload))
179
+ }
180
+ sendEvent(view, "onCustomEvent", map)
196
181
  }
197
- Thread.sleep(10)
198
- }
199
182
 
200
- Log.w(TAG, "Data request timeout: $requestId")
201
- return null
183
+ override fun onDataRequest(
184
+ requestId: String,
185
+ requestType: String,
186
+ params: Map<String, Any?>
187
+ ): Map<String, Any?>? {
188
+ Log.d(TAG, "onDataRequest received - id: $requestId, type: $requestType, params: $params")
189
+
190
+ // Send event to React Native
191
+ val map = Arguments.createMap().apply {
192
+ putString("requestId", requestId)
193
+ putString("requestType", requestType)
194
+ putMap("params", convertMapToWritableMap(params))
195
+ }
196
+ sendEvent(view, "onDataRequest", map)
197
+
198
+ // Wait for response from React Native (with timeout)
199
+ val startTime = System.currentTimeMillis()
200
+ val timeout = 5000L // 5 seconds
201
+
202
+ while (System.currentTimeMillis() - startTime < timeout) {
203
+ val response = pendingDataResponses.remove(requestId)
204
+ if (response != null) {
205
+ Log.d(TAG, "Data response received for request: $requestId")
206
+ return response
207
+ }
208
+ Thread.sleep(10)
209
+ }
210
+
211
+ Log.w(TAG, "Data request timeout: $requestId")
212
+ return null
213
+ }
214
+ })
215
+
216
+ // Load offerwall with fetched URL
217
+ Log.d(TAG, "Calling view.loadOfferwall()...")
218
+ view.loadOfferwall(
219
+ baseUrl = baseUrl,
220
+ userId = user.userId,
221
+ appKey = config.appKey,
222
+ placementId = placementId
223
+ )
224
+ Log.d(TAG, "view.loadOfferwall() call completed")
225
+ } else {
226
+ // Failed to fetch placement URL
227
+ val error = result.exceptionOrNull()
228
+ val errorMessage = "Failed to fetch placement URL: ${error?.message ?: "Unknown error"}"
229
+ Log.e(TAG, errorMessage)
230
+ sendErrorEvent(view, errorMessage)
202
231
  }
203
- })
204
-
205
- // Load offerwall
206
- Log.d(TAG, "Calling view.loadOfferwall()...")
207
- view.loadOfferwall(
208
- baseUrl = offerwallUrl,
209
- userId = user.userId,
210
- appKey = config.appKey,
211
- placementId = placementId
212
- )
213
- Log.d(TAG, "view.loadOfferwall() call completed")
232
+ }
214
233
 
215
234
  } catch (e: Exception) {
216
235
  Log.e(TAG, "Exception in loadOfferwall", e)
@@ -78,18 +78,33 @@ class AdchainOfferwallRNView: UIView {
78
78
  }
79
79
 
80
80
  guard let config = AdchainSdk.shared.getConfig(),
81
- let user = AdchainSdk.shared.getCurrentUser(),
82
- let offerwallUrl = AdchainSdk.shared.getOfferwallUrl() else {
81
+ let user = AdchainSdk.shared.getCurrentUser() else {
83
82
  if AdchainSdk.shared.getConfig() == nil {
84
83
  sendErrorEvent(error: "SDK not initialized")
85
84
  } else if AdchainSdk.shared.getCurrentUser() == nil {
86
85
  sendErrorEvent(error: "User not logged in")
87
- } else {
88
- sendErrorEvent(error: "Offerwall URL not available")
89
86
  }
90
87
  return
91
88
  }
92
89
 
90
+ // Fetch placement URL from server (like openOfferwallNestAds does)
91
+ Task {
92
+ do {
93
+ let baseUrl = try await NetworkManager.shared.getPlacementUrl(placementId: placementId)
94
+
95
+ await MainActor.run {
96
+ self.loadOfferwallWithUrl(view: view, baseUrl: baseUrl, config: config, user: user, placementId: placementId)
97
+ }
98
+ } catch {
99
+ await MainActor.run {
100
+ let errorMessage = "Failed to fetch placement URL: \(error.localizedDescription)"
101
+ self.sendErrorEvent(error: errorMessage)
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ private func loadOfferwallWithUrl(view: AdchainOfferwallView, baseUrl: String, config: AdchainConfig, user: AdchainUser, placementId: String) {
93
108
  // Set callback for offerwall events
94
109
  view.setCallback(RNOfferwallCallback(
95
110
  onOpened: { [weak self] in
@@ -139,9 +154,9 @@ class AdchainOfferwallRNView: UIView {
139
154
  }
140
155
  ))
141
156
 
142
- // Load offerwall
157
+ // Load offerwall with fetched URL
143
158
  view.loadOfferwall(
144
- baseUrl: offerwallUrl,
159
+ baseUrl: baseUrl,
145
160
  userId: user.userId,
146
161
  appKey: config.appKey,
147
162
  placementId: placementId
@@ -68,7 +68,7 @@ class AdchainSdkModule: RCTEventEmitter {
68
68
  .setEnvironment(env)
69
69
 
70
70
  if let timeoutValue = timeout {
71
- configBuilder = configBuilder.setTimeout(TimeInterval(timeoutValue.doubleValue))
71
+ configBuilder = configBuilder.setTimeoutMillis(Int64(timeoutValue.doubleValue))
72
72
  }
73
73
 
74
74
  let config = configBuilder.build()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1selfworld/adchain-sdk-react-native",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "AdChain SDK for React Native with Expo support",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -28,10 +28,10 @@ function withAdchainIOS(config) {
28
28
  if (targetMatch) {
29
29
  const targetName = targetMatch[1];
30
30
  const podDeclaration = `
31
- # AdChain SDK (Git Pod) - v1.0.45
31
+ # AdChain SDK (Git Pod) - v1.0.46
32
32
  # CocoaPods Trunk에 없으므로 Git 방식 사용
33
33
  # Swift 5.5 (5.9 optional 파라미터 버그 회피)
34
- pod 'AdChainSDK', :git => 'https://github.com/1selfworld-labs/adchain-sdk-ios-release.git', :tag => 'v1.0.45'
34
+ pod 'AdChainSDK', :git => 'https://github.com/1selfworld-labs/adchain-sdk-ios-release.git', :tag => 'v1.0.46'
35
35
  `;
36
36
 
37
37
  podfile = podfile.replace(
@@ -41,8 +41,8 @@ function withAdchainIOS(config) {
41
41
  } else {
42
42
  // Fallback
43
43
  const podDeclaration = `
44
- # AdChain SDK (Git Pod) - v1.0.45
45
- pod 'AdChainSDK', :git => 'https://github.com/1selfworld-labs/adchain-sdk-ios-release.git', :tag => 'v1.0.45'
44
+ # AdChain SDK (Git Pod) - v1.0.46
45
+ pod 'AdChainSDK', :git => 'https://github.com/1selfworld-labs/adchain-sdk-ios-release.git', :tag => 'v1.0.46'
46
46
  `;
47
47
 
48
48
  podfile = podfile.replace(