@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 +12 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/adchain/exposdk/AdchainOfferwallViewManager.kt +112 -93
- package/ios/AdchainOfferwallViewManager.swift +21 -6
- package/ios/AdchainSdk.swift +1 -1
- package/package.json +1 -1
- package/plugin/src/withAdchainIOS.js +4 -4
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 | 초기 릴리즈 |
|
package/android/build.gradle
CHANGED
|
@@ -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.
|
|
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
|
|
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}
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
sendEvent(view, "onOfferwallError", map)
|
|
140
|
-
}
|
|
142
|
+
override fun onClosed() {
|
|
143
|
+
sendEvent(view, "onOfferwallClosed", Arguments.createMap())
|
|
144
|
+
}
|
|
141
145
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
201
|
-
|
|
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:
|
|
159
|
+
baseUrl: baseUrl,
|
|
145
160
|
userId: user.userId,
|
|
146
161
|
appKey: config.appKey,
|
|
147
162
|
placementId: placementId
|
package/ios/AdchainSdk.swift
CHANGED
|
@@ -68,7 +68,7 @@ class AdchainSdkModule: RCTEventEmitter {
|
|
|
68
68
|
.setEnvironment(env)
|
|
69
69
|
|
|
70
70
|
if let timeoutValue = timeout {
|
|
71
|
-
configBuilder = configBuilder.
|
|
71
|
+
configBuilder = configBuilder.setTimeoutMillis(Int64(timeoutValue.doubleValue))
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
let config = configBuilder.build()
|
package/package.json
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
-
pod 'AdChainSDK', :git => 'https://github.com/1selfworld-labs/adchain-sdk-ios-release.git', :tag => 'v1.0.
|
|
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(
|