spotny-sdk 0.4.1 → 0.5.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.
Potentially problematic release.
This version of spotny-sdk might be problematic. Click here for more details.
- package/android/src/main/java/com/spotnysdk/SpotnySdkModule.kt +68 -20
- package/ios/SpotnyBeaconScanner.swift +109 -34
- package/ios/SpotnySdk.mm +19 -4
- package/lib/module/NativeSpotnySdk.js.map +1 -1
- package/lib/module/index.js +32 -7
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeSpotnySdk.d.ts +4 -1
- package/lib/typescript/src/NativeSpotnySdk.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +28 -6
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeSpotnySdk.ts +6 -1
- package/src/index.tsx +41 -10
|
@@ -37,10 +37,14 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
37
37
|
private val ioExecutor = Executors.newCachedThreadPool()
|
|
38
38
|
|
|
39
39
|
// ── Session state ─────────────────────────────────────────────────────────
|
|
40
|
-
private var currentUserUUID: String? = null
|
|
41
|
-
private var userId: Int? = null
|
|
42
40
|
@Volatile private var scanning = false
|
|
43
41
|
|
|
42
|
+
// ── Identity (persisted in SharedPreferences) ────────────────────────
|
|
43
|
+
private var identifiedUserId: String? = null
|
|
44
|
+
private var profilePhone: String? = null
|
|
45
|
+
private var profileEmail: String? = null
|
|
46
|
+
private var profileName: String? = null
|
|
47
|
+
|
|
44
48
|
// ── Configuration ─────────────────────────────────────────────────────────
|
|
45
49
|
// backendURL is fixed — not overridable by consumers.
|
|
46
50
|
private val backendURL = "https://api.spotny.app"
|
|
@@ -90,31 +94,30 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
90
94
|
|
|
91
95
|
private fun resumeStoredSession() {
|
|
92
96
|
val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
// Reload identity
|
|
98
|
+
identifiedUserId = prefs.getString("userId", null)
|
|
99
|
+
profilePhone = prefs.getString("profilePhone", null)
|
|
100
|
+
profileEmail = prefs.getString("profileEmail", null)
|
|
101
|
+
profileName = prefs.getString("profileName", null)
|
|
102
|
+
// Resume scanning only if a valid session was previously active
|
|
103
|
+
if (!prefs.getBoolean("sessionActive", false)) return
|
|
104
|
+
Log.d(TAG, "Resuming session (userId: ${identifiedUserId ?: "anonymous"})")
|
|
98
105
|
startBeaconScanning()
|
|
99
106
|
scanning = true
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
// ── Turbo Module methods ──────────────────────────────────────────────────
|
|
103
110
|
|
|
104
|
-
override fun startScanner(
|
|
111
|
+
override fun startScanner(promise: Promise) {
|
|
105
112
|
if (scanning) { promise.resolve("Already scanning"); return }
|
|
106
113
|
|
|
107
|
-
currentUserUUID = userUUID
|
|
108
|
-
this.userId = userId?.toInt()
|
|
109
|
-
|
|
110
114
|
val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE).edit()
|
|
111
|
-
prefs.
|
|
112
|
-
this.userId?.let { prefs.putInt("userId", it) } ?: prefs.remove("userId")
|
|
115
|
+
prefs.putBoolean("sessionActive", true)
|
|
113
116
|
prefs.apply()
|
|
114
117
|
|
|
115
118
|
startBeaconScanning()
|
|
116
119
|
scanning = true
|
|
117
|
-
Log.d(TAG, "Started scanning
|
|
120
|
+
Log.d(TAG, "Started scanning (userId: ${identifiedUserId ?: "anonymous"})")
|
|
118
121
|
promise.resolve("Scanning started")
|
|
119
122
|
}
|
|
120
123
|
|
|
@@ -129,9 +132,9 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
129
132
|
cleanupAllState()
|
|
130
133
|
|
|
131
134
|
val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE).edit()
|
|
132
|
-
prefs.remove("
|
|
135
|
+
prefs.remove("sessionActive"); prefs.apply()
|
|
133
136
|
|
|
134
|
-
scanning = false
|
|
137
|
+
scanning = false
|
|
135
138
|
Log.d(TAG, "Stopped scanning")
|
|
136
139
|
promise.resolve("Scanning stopped")
|
|
137
140
|
}
|
|
@@ -174,6 +177,51 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
174
177
|
promise.resolve("Debounce cache cleared")
|
|
175
178
|
}
|
|
176
179
|
|
|
180
|
+
// ── Identity ──────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
override fun identify(userId: String, promise: Promise) {
|
|
183
|
+
identifiedUserId = userId
|
|
184
|
+
val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE).edit()
|
|
185
|
+
prefs.putString("userId", userId); prefs.apply()
|
|
186
|
+
Log.d(TAG, "Identified user $userId")
|
|
187
|
+
promise.resolve("User identified")
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
override fun updateProfile(profile: ReadableMap?, promise: Promise) {
|
|
191
|
+
val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE).edit()
|
|
192
|
+
profile?.getString("phoneNumber")?.let { profilePhone = it; prefs.putString("profilePhone", it) }
|
|
193
|
+
profile?.getString("email")?.let { profileEmail = it; prefs.putString("profileEmail", it) }
|
|
194
|
+
profile?.getString("name")?.let { profileName = it; prefs.putString("profileName", it) }
|
|
195
|
+
prefs.apply()
|
|
196
|
+
|
|
197
|
+
val payload = mutableMapOf<String, Any?>("device_id" to getDeviceId())
|
|
198
|
+
identifiedUserId?.let { payload["user_id"] = it }
|
|
199
|
+
profilePhone?.let { payload["phone"] = it }
|
|
200
|
+
profileEmail?.let { payload["email"] = it }
|
|
201
|
+
profileName?.let { payload["name"] = it }
|
|
202
|
+
source?.let { payload["source"] = it }
|
|
203
|
+
|
|
204
|
+
post("/api/app/profiles/update", payload) { status, _ ->
|
|
205
|
+
if (status in 200..299) {
|
|
206
|
+
Log.d(TAG, "Profile updated")
|
|
207
|
+
promise.resolve("Profile updated")
|
|
208
|
+
} else {
|
|
209
|
+
Log.w(TAG, "Profile update failed — status $status")
|
|
210
|
+
promise.reject("PROFILE_UPDATE_FAILED", "Server returned status $status")
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
override fun resetIdentity(promise: Promise) {
|
|
216
|
+
identifiedUserId = null; profilePhone = null; profileEmail = null; profileName = null
|
|
217
|
+
val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE).edit()
|
|
218
|
+
prefs.remove("userId"); prefs.remove("profilePhone")
|
|
219
|
+
prefs.remove("profileEmail"); prefs.remove("profileName")
|
|
220
|
+
prefs.apply()
|
|
221
|
+
Log.d(TAG, "Identity reset")
|
|
222
|
+
promise.resolve("Identity reset")
|
|
223
|
+
}
|
|
224
|
+
|
|
177
225
|
override fun getDebounceStatus(promise: Promise) {
|
|
178
226
|
val map = WritableNativeMap()
|
|
179
227
|
for ((key, _) in activeCampaigns) {
|
|
@@ -436,8 +484,8 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
436
484
|
fetchInProgress[key] = true
|
|
437
485
|
|
|
438
486
|
val payload = mutableMapOf<String, Any?>("beacon_id" to key, "device_id" to deviceId)
|
|
439
|
-
|
|
440
|
-
source?.let
|
|
487
|
+
identifiedUserId?.let { payload["user_id"] = it }
|
|
488
|
+
source?.let { payload["source"] = it }
|
|
441
489
|
|
|
442
490
|
post("/api/app/campaigns/beacon", payload) { status, body ->
|
|
443
491
|
fetchInProgress[key] = false
|
|
@@ -503,8 +551,8 @@ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
503
551
|
)
|
|
504
552
|
campaign.campaignId?.let { payload["campaign_id"] = it }
|
|
505
553
|
campaign.sessionId?.let { payload["session_id"] = it }
|
|
506
|
-
|
|
507
|
-
source?.let { payload["source"]
|
|
554
|
+
identifiedUserId?.let { payload["user_id"] = it }
|
|
555
|
+
source?.let { payload["source"] = it }
|
|
508
556
|
|
|
509
557
|
post(endpoint, payload) { status, body ->
|
|
510
558
|
if (status in 200..299) {
|
|
@@ -41,10 +41,14 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
41
41
|
private var locationManager: CLLocationManager!
|
|
42
42
|
|
|
43
43
|
// ── Session state ─────────────────────────────────────────────────────────
|
|
44
|
-
private var currentUserUUID: String?
|
|
45
|
-
private var userId: Int?
|
|
46
44
|
private var scanning: Bool = false
|
|
47
45
|
|
|
46
|
+
// ── Identity (persisted in Keychain across sessions) ─────────────────────
|
|
47
|
+
private var identifiedUserId: String?
|
|
48
|
+
private var profilePhone: String?
|
|
49
|
+
private var profileEmail: String?
|
|
50
|
+
private var profileName: String?
|
|
51
|
+
|
|
48
52
|
// ── Configuration ────────────────────────────────────────────────────────────
|
|
49
53
|
// backendURL and kontaktAPIKey are fixed — not overridable by consumers.
|
|
50
54
|
private let backendURL: String = "https://api.spotny.app"
|
|
@@ -114,26 +118,27 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
private func resumeStoredSession() {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
// Always reload identity from Keychain (persists across sessions)
|
|
122
|
+
identifiedUserId = keychainRead(key: "SpotnySDK_userId")
|
|
123
|
+
profilePhone = keychainRead(key: "SpotnySDK_profilePhone")
|
|
124
|
+
profileEmail = keychainRead(key: "SpotnySDK_profileEmail")
|
|
125
|
+
profileName = keychainRead(key: "SpotnySDK_profileName")
|
|
126
|
+
|
|
127
|
+
// Resume scanning only if a valid session was previously active
|
|
128
|
+
guard let ts = UserDefaults.standard.object(forKey: "SpotnySDK_sessionTimestamp") as? Double else { return }
|
|
129
|
+
let age = Date().timeIntervalSince1970 - ts
|
|
130
|
+
if age > sessionTTL {
|
|
131
|
+
print("⚠️ SpotnySDK: Stale session (\(Int(age / 3600))h old) — discarding")
|
|
132
|
+
clearStoredSession()
|
|
133
|
+
return
|
|
126
134
|
}
|
|
127
|
-
|
|
128
|
-
userId = UserDefaults.standard.object(forKey: "SpotnySDK_userId") as? Int
|
|
129
|
-
print("🔄 SpotnySDK: Resuming session for UUID: \(stored)")
|
|
135
|
+
print("🔄 SpotnySDK: Resuming session (userId: \(identifiedUserId ?? "anonymous"))")
|
|
130
136
|
startPersistentScanning()
|
|
131
137
|
scanning = true
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
private func clearStoredSession() {
|
|
135
|
-
|
|
136
|
-
UserDefaults.standard.removeObject(forKey: "SpotnySDK_userId")
|
|
141
|
+
// Only clears the scanning session — identity (Keychain) is intentionally kept
|
|
137
142
|
UserDefaults.standard.removeObject(forKey: "SpotnySDK_sessionTimestamp")
|
|
138
143
|
UserDefaults.standard.synchronize()
|
|
139
144
|
}
|
|
@@ -142,9 +147,7 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
142
147
|
|
|
143
148
|
@objc
|
|
144
149
|
public func startScanner(
|
|
145
|
-
|
|
146
|
-
userId: NSNumber?,
|
|
147
|
-
resolve: @escaping (Any?) -> Void,
|
|
150
|
+
withResolve resolve: @escaping (Any?) -> Void,
|
|
148
151
|
reject: @escaping (String?, String?, Error?) -> Void
|
|
149
152
|
) {
|
|
150
153
|
if scanning {
|
|
@@ -152,15 +155,6 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
152
155
|
return
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
currentUserUUID = userUUID
|
|
156
|
-
self.userId = userId?.intValue
|
|
157
|
-
|
|
158
|
-
UserDefaults.standard.set(userUUID, forKey: "SpotnySDK_userUUID")
|
|
159
|
-
if let uid = userId {
|
|
160
|
-
UserDefaults.standard.set(uid.intValue, forKey: "SpotnySDK_userId")
|
|
161
|
-
} else {
|
|
162
|
-
UserDefaults.standard.removeObject(forKey: "SpotnySDK_userId")
|
|
163
|
-
}
|
|
164
158
|
UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "SpotnySDK_sessionTimestamp")
|
|
165
159
|
UserDefaults.standard.synchronize()
|
|
166
160
|
|
|
@@ -171,7 +165,7 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
171
165
|
|
|
172
166
|
startPersistentScanning()
|
|
173
167
|
scanning = true
|
|
174
|
-
print("✅ SpotnySDK: Started scanning
|
|
168
|
+
print("✅ SpotnySDK: Started scanning (userId: \(identifiedUserId ?? "anonymous"))")
|
|
175
169
|
resolve("Scanning started")
|
|
176
170
|
}
|
|
177
171
|
|
|
@@ -187,8 +181,6 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
187
181
|
clearStoredSession()
|
|
188
182
|
|
|
189
183
|
scanning = false
|
|
190
|
-
currentUserUUID = nil
|
|
191
|
-
userId = nil
|
|
192
184
|
|
|
193
185
|
print("⏹️ SpotnySDK: Stopped scanning")
|
|
194
186
|
resolve("Scanning stopped")
|
|
@@ -260,6 +252,80 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
260
252
|
resolve("Debounce cache cleared")
|
|
261
253
|
}
|
|
262
254
|
|
|
255
|
+
// MARK: - Identity
|
|
256
|
+
|
|
257
|
+
@objc
|
|
258
|
+
public func identify(
|
|
259
|
+
_ userId: String,
|
|
260
|
+
resolve: @escaping (Any?) -> Void,
|
|
261
|
+
reject: @escaping (String?, String?, Error?) -> Void
|
|
262
|
+
) {
|
|
263
|
+
identifiedUserId = userId
|
|
264
|
+
keychainWrite(key: "SpotnySDK_userId", value: userId)
|
|
265
|
+
print("👤 SpotnySDK: Identified user \(userId)")
|
|
266
|
+
resolve("User identified")
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@objc
|
|
270
|
+
public func updateProfile(
|
|
271
|
+
_ profile: NSDictionary,
|
|
272
|
+
resolve: @escaping (Any?) -> Void,
|
|
273
|
+
reject: @escaping (String?, String?, Error?) -> Void
|
|
274
|
+
) {
|
|
275
|
+
if let phone = profile["phoneNumber"] as? String {
|
|
276
|
+
profilePhone = phone
|
|
277
|
+
keychainWrite(key: "SpotnySDK_profilePhone", value: phone)
|
|
278
|
+
}
|
|
279
|
+
if let email = profile["email"] as? String {
|
|
280
|
+
profileEmail = email
|
|
281
|
+
keychainWrite(key: "SpotnySDK_profileEmail", value: email)
|
|
282
|
+
}
|
|
283
|
+
if let name = profile["name"] as? String {
|
|
284
|
+
profileName = name
|
|
285
|
+
keychainWrite(key: "SpotnySDK_profileName", value: name)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
var payload: [String: Any] = ["device_id": getDeviceId()]
|
|
289
|
+
if let uid = identifiedUserId { payload["user_id"] = uid }
|
|
290
|
+
if let ph = profilePhone { payload["phone"] = ph }
|
|
291
|
+
if let em = profileEmail { payload["email"] = em }
|
|
292
|
+
if let nm = profileName { payload["name"] = nm }
|
|
293
|
+
if let src = source { payload["source"] = src }
|
|
294
|
+
|
|
295
|
+
post(endpoint: "/api/app/profiles/update", payload: payload) { result in
|
|
296
|
+
switch result {
|
|
297
|
+
case .success(let (status, _)):
|
|
298
|
+
if 200...299 ~= status {
|
|
299
|
+
print("✅ SpotnySDK: Profile updated")
|
|
300
|
+
resolve("Profile updated")
|
|
301
|
+
} else {
|
|
302
|
+
print("❌ SpotnySDK: Profile update failed — status \(status)")
|
|
303
|
+
reject("PROFILE_UPDATE_FAILED", "Server returned status \(status)", nil)
|
|
304
|
+
}
|
|
305
|
+
case .failure(let error):
|
|
306
|
+
print("❌ SpotnySDK: Profile update error — \(error.localizedDescription)")
|
|
307
|
+
reject("PROFILE_UPDATE_ERROR", error.localizedDescription, error)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@objc
|
|
313
|
+
public func resetIdentity(
|
|
314
|
+
withResolve resolve: @escaping (Any?) -> Void,
|
|
315
|
+
reject: @escaping (String?, String?, Error?) -> Void
|
|
316
|
+
) {
|
|
317
|
+
identifiedUserId = nil
|
|
318
|
+
profilePhone = nil
|
|
319
|
+
profileEmail = nil
|
|
320
|
+
profileName = nil
|
|
321
|
+
keychainDelete(key: "SpotnySDK_userId")
|
|
322
|
+
keychainDelete(key: "SpotnySDK_profilePhone")
|
|
323
|
+
keychainDelete(key: "SpotnySDK_profileEmail")
|
|
324
|
+
keychainDelete(key: "SpotnySDK_profileName")
|
|
325
|
+
print("🔓 SpotnySDK: Identity reset")
|
|
326
|
+
resolve("Identity reset")
|
|
327
|
+
}
|
|
328
|
+
|
|
263
329
|
@objc
|
|
264
330
|
public func getDebounceStatus(
|
|
265
331
|
withResolve resolve: @escaping (Any?) -> Void,
|
|
@@ -364,6 +430,15 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
364
430
|
}
|
|
365
431
|
}
|
|
366
432
|
|
|
433
|
+
private func keychainDelete(key: String) {
|
|
434
|
+
let query: [CFString: Any] = [
|
|
435
|
+
kSecClass: kSecClassGenericPassword,
|
|
436
|
+
kSecAttrService: keychainService,
|
|
437
|
+
kSecAttrAccount: key
|
|
438
|
+
]
|
|
439
|
+
SecItemDelete(query as CFDictionary)
|
|
440
|
+
}
|
|
441
|
+
|
|
367
442
|
private func getDeviceId() -> String {
|
|
368
443
|
let kcKey = "SpotnySDK_deviceId"
|
|
369
444
|
// 1. Keychain — survives uninstall
|
|
@@ -464,8 +539,8 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
464
539
|
fetchInProgress[key] = true
|
|
465
540
|
|
|
466
541
|
var payload: [String: Any] = ["beacon_id": key, "device_id": deviceId]
|
|
467
|
-
if let uid =
|
|
468
|
-
if let src = source
|
|
542
|
+
if let uid = identifiedUserId { payload["user_id"] = uid }
|
|
543
|
+
if let src = source { payload["source"] = src }
|
|
469
544
|
|
|
470
545
|
post(endpoint: "/api/app/campaigns/beacon", payload: payload) { [weak self] result in
|
|
471
546
|
guard let self = self else { return }
|
|
@@ -540,7 +615,7 @@ public class SpotnyBeaconScanner: NSObject {
|
|
|
540
615
|
]
|
|
541
616
|
if let cid = campaign.campaignId { payload["campaign_id"] = cid }
|
|
542
617
|
if let sid = campaign.sessionId { payload["session_id"] = sid }
|
|
543
|
-
if let uid =
|
|
618
|
+
if let uid = identifiedUserId { payload["user_id"] = uid }
|
|
544
619
|
if let src = source { payload["source"] = src }
|
|
545
620
|
|
|
546
621
|
post(endpoint: endpoint, payload: payload) { [weak self] result in
|
package/ios/SpotnySdk.mm
CHANGED
|
@@ -64,11 +64,9 @@ RCT_EXPORT_MODULE()
|
|
|
64
64
|
|
|
65
65
|
// ── Method implementations ───────────────────────────────────────────────────
|
|
66
66
|
|
|
67
|
-
- (void)startScanner:(
|
|
68
|
-
userId:(NSNumber *)userId
|
|
69
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
67
|
+
- (void)startScanner:(RCTPromiseResolveBlock)resolve
|
|
70
68
|
reject:(RCTPromiseRejectBlock)reject {
|
|
71
|
-
[_scanner
|
|
69
|
+
[_scanner startScannerWithResolve:resolve reject:reject];
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
- (void)stopScanner:(RCTPromiseResolveBlock)resolve
|
|
@@ -118,6 +116,23 @@ RCT_EXPORT_MODULE()
|
|
|
118
116
|
[_scanner getDebounceStatusWithResolve:resolve reject:reject];
|
|
119
117
|
}
|
|
120
118
|
|
|
119
|
+
- (void)identify:(NSString *)userId
|
|
120
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
121
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
122
|
+
[_scanner identify:userId resolve:resolve reject:reject];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
- (void)updateProfile:(NSDictionary *)profile
|
|
126
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
127
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
128
|
+
[_scanner updateProfile:profile resolve:resolve reject:reject];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
- (void)resetIdentity:(RCTPromiseResolveBlock)resolve
|
|
132
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
133
|
+
[_scanner resetIdentityWithResolve:resolve reject:reject];
|
|
134
|
+
}
|
|
135
|
+
|
|
121
136
|
// ── TurboModule JSI bridge ───────────────────────────────────────────────────
|
|
122
137
|
|
|
123
138
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeSpotnySdk.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeSpotnySdk.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAiCpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -16,13 +16,9 @@ const eventEmitter = new NativeEventEmitter(NativeModules.SpotnySdk);
|
|
|
16
16
|
|
|
17
17
|
// ── Core scanning ────────────────────────────────────────────────────────────
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* @param userId Optional authenticated user ID.
|
|
23
|
-
*/
|
|
24
|
-
export function startScanner(userUUID, userId) {
|
|
25
|
-
return NativeSpotnySdk.startScanner(userUUID, userId ?? null);
|
|
19
|
+
/** Start beacon scanning. Call `identify()` before this for attributed tracking. */
|
|
20
|
+
export function startScanner() {
|
|
21
|
+
return NativeSpotnySdk.startScanner();
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
/** Stop beacon scanning and clean up all state. */
|
|
@@ -35,6 +31,35 @@ export function isScanning() {
|
|
|
35
31
|
return NativeSpotnySdk.isScanning();
|
|
36
32
|
}
|
|
37
33
|
|
|
34
|
+
// ── Identity ─────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Identify the current user. Pass any opaque string your app uses — user ID,
|
|
38
|
+
* hashed email, hashed phone, etc. Stored in Keychain and sent with all
|
|
39
|
+
* subsequent tracking events.
|
|
40
|
+
*
|
|
41
|
+
* Call before `startScanner()` or at any point during an active session.
|
|
42
|
+
*/
|
|
43
|
+
export function identify(userId) {
|
|
44
|
+
return NativeSpotnySdk.identify(userId);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Update the user's profile on the Spotny backend.
|
|
49
|
+
* Fields are optional — pass only what you have.
|
|
50
|
+
*/
|
|
51
|
+
export function updateProfile(profile) {
|
|
52
|
+
return NativeSpotnySdk.updateProfile(profile);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Clear the stored identity (user ID + profile fields) from Keychain.
|
|
57
|
+
* Call on logout. The device ID is NOT affected.
|
|
58
|
+
*/
|
|
59
|
+
export function resetIdentity() {
|
|
60
|
+
return NativeSpotnySdk.resetIdentity();
|
|
61
|
+
}
|
|
62
|
+
|
|
38
63
|
// ── Configuration ────────────────────────────────────────────────────────────
|
|
39
64
|
|
|
40
65
|
/**
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeEventEmitter","NativeModules","NativeSpotnySdk","SpotnyEvents","ON_BEACONS_RANGED","ON_BEACON_REGION_EVENT","eventEmitter","SpotnySdk","startScanner","
|
|
1
|
+
{"version":3,"names":["NativeEventEmitter","NativeModules","NativeSpotnySdk","SpotnyEvents","ON_BEACONS_RANGED","ON_BEACON_REGION_EVENT","eventEmitter","SpotnySdk","startScanner","stopScanner","isScanning","identify","userId","updateProfile","profile","resetIdentity","configure","config","requestNotificationPermissions","getDebugLogs","clearDebugLogs","setDebounceInterval","interval","clearDebounceCache","getDebounceStatus","addBeaconsRangedListener","callback","addListener","addBeaconRegionListener"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,kBAAkB,EAAEC,aAAa,QAAQ,cAAc;AAChE,OAAOC,eAAe,MAAM,sBAAmB;;AAE/C;AACA,OAAO,MAAMC,YAAY,GAAG;EAC1BC,iBAAiB,EAAE,iBAAiB;EACpCC,sBAAsB,EAAE;AAC1B,CAAU;;AAEV;;AA8CA;AACA,MAAMC,YAAY,GAAG,IAAIN,kBAAkB,CAACC,aAAa,CAACM,SAAS,CAAC;;AAEpE;;AAEA;AACA,OAAO,SAASC,YAAYA,CAAA,EAAoB;EAC9C,OAAON,eAAe,CAACM,YAAY,CAAC,CAAC;AACvC;;AAEA;AACA,OAAO,SAASC,WAAWA,CAAA,EAAoB;EAC7C,OAAOP,eAAe,CAACO,WAAW,CAAC,CAAC;AACtC;;AAEA;AACA,OAAO,SAASC,UAAUA,CAAA,EAAqB;EAC7C,OAAOR,eAAe,CAACQ,UAAU,CAAC,CAAC;AACrC;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,QAAQA,CAACC,MAAc,EAAmB;EACxD,OAAOV,eAAe,CAACS,QAAQ,CAACC,MAAM,CAAC;AACzC;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,aAAaA,CAACC,OAAsB,EAAmB;EACrE,OAAOZ,eAAe,CAACW,aAAa,CAACC,OAAiB,CAAC;AACzD;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,aAAaA,CAAA,EAAoB;EAC/C,OAAOb,eAAe,CAACa,aAAa,CAAC,CAAC;AACxC;;AAEA;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,SAASA,CAACC,MAAuB,EAAmB;EAClE,OAAOf,eAAe,CAACc,SAAS,CAACC,MAAgB,CAAC;AACpD;;AAEA;;AAEA;AACA,OAAO,SAASC,8BAA8BA,CAAA,EAAoB;EAChE,OAAOhB,eAAe,CAACgB,8BAA8B,CAAC,CAAC;AACzD;;AAEA;;AAEA;AACA,OAAO,SAASC,YAAYA,CAAA,EAAoB;EAC9C,OAAOjB,eAAe,CAACiB,YAAY,CAAC,CAAC;AACvC;;AAEA;AACA,OAAO,SAASC,cAAcA,CAAA,EAAoB;EAChD,OAAOlB,eAAe,CAACkB,cAAc,CAAC,CAAC;AACzC;;AAEA;;AAEA;AACA,OAAO,SAASC,mBAAmBA,CAACC,QAAgB,EAAmB;EACrE,OAAOpB,eAAe,CAACmB,mBAAmB,CAACC,QAAQ,CAAC;AACtD;;AAEA;AACA,OAAO,SAASC,kBAAkBA,CAAA,EAAoB;EACpD,OAAOrB,eAAe,CAACqB,kBAAkB,CAAC,CAAC;AAC7C;;AAEA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAoB;EACnD,OAAOtB,eAAe,CAACsB,iBAAiB,CAAC,CAAC;AAC5C;;AAEA;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,wBAAwBA,CACtCC,QAA4C,EAC5C;EACA,OAAOpB,YAAY,CAACqB,WAAW,CAC7BxB,YAAY,CAACC,iBAAiB,EAC9BsB,QACF,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASE,uBAAuBA,CACrCF,QAA4C,EAC5C;EACA,OAAOpB,YAAY,CAACqB,WAAW,CAC7BxB,YAAY,CAACE,sBAAsB,EACnCqB,QACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { type TurboModule } from 'react-native';
|
|
2
2
|
export interface Spec extends TurboModule {
|
|
3
|
-
startScanner(
|
|
3
|
+
startScanner(): Promise<string>;
|
|
4
4
|
stopScanner(): Promise<string>;
|
|
5
5
|
isScanning(): Promise<boolean>;
|
|
6
|
+
identify(userId: string): Promise<string>;
|
|
7
|
+
updateProfile(profile: Object): Promise<string>;
|
|
8
|
+
resetIdentity(): Promise<string>;
|
|
6
9
|
configure(config: Object): Promise<string>;
|
|
7
10
|
requestNotificationPermissions(): Promise<string>;
|
|
8
11
|
getDebugLogs(): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeSpotnySdk.d.ts","sourceRoot":"","sources":["../../../src/NativeSpotnySdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IAEvC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"NativeSpotnySdk.d.ts","sourceRoot":"","sources":["../../../src/NativeSpotnySdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IAEvC,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAG/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAGjC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAG3C,8BAA8B,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAGlD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAGlC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAGrC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAAmE"}
|
|
@@ -33,16 +33,38 @@ export type SpotnySdkConfig = {
|
|
|
33
33
|
*/
|
|
34
34
|
distanceCorrectionFactor?: number;
|
|
35
35
|
};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
export type SpotnyProfile = {
|
|
37
|
+
/** Phone number (e.g. '+966501234567'). */
|
|
38
|
+
phoneNumber?: string;
|
|
39
|
+
/** Email address. */
|
|
40
|
+
email?: string;
|
|
41
|
+
/** Display name. */
|
|
42
|
+
name?: string;
|
|
43
|
+
};
|
|
44
|
+
/** Start beacon scanning. Call `identify()` before this for attributed tracking. */
|
|
45
|
+
export declare function startScanner(): Promise<string>;
|
|
42
46
|
/** Stop beacon scanning and clean up all state. */
|
|
43
47
|
export declare function stopScanner(): Promise<string>;
|
|
44
48
|
/** Returns `true` if the SDK is currently scanning. */
|
|
45
49
|
export declare function isScanning(): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Identify the current user. Pass any opaque string your app uses — user ID,
|
|
52
|
+
* hashed email, hashed phone, etc. Stored in Keychain and sent with all
|
|
53
|
+
* subsequent tracking events.
|
|
54
|
+
*
|
|
55
|
+
* Call before `startScanner()` or at any point during an active session.
|
|
56
|
+
*/
|
|
57
|
+
export declare function identify(userId: string): Promise<string>;
|
|
58
|
+
/**
|
|
59
|
+
* Update the user's profile on the Spotny backend.
|
|
60
|
+
* Fields are optional — pass only what you have.
|
|
61
|
+
*/
|
|
62
|
+
export declare function updateProfile(profile: SpotnyProfile): Promise<string>;
|
|
63
|
+
/**
|
|
64
|
+
* Clear the stored identity (user ID + profile fields) from Keychain.
|
|
65
|
+
* Call on logout. The device ID is NOT affected.
|
|
66
|
+
*/
|
|
67
|
+
export declare function resetIdentity(): Promise<string>;
|
|
46
68
|
/**
|
|
47
69
|
* Override SDK configuration at runtime.
|
|
48
70
|
* Must be called **before** `startScanner`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAIX,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IACvC,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAIX,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IACvC,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAOF,oFAAoF;AACpF,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAE9C;AAED,mDAAmD;AACnD,wBAAgB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAE7C;AAED,uDAAuD;AACvD,wBAAgB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAE7C;AAID;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAExD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAErE;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAE/C;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAElE;AAID,gEAAgE;AAChE,wBAAgB,8BAA8B,IAAI,OAAO,CAAC,MAAM,CAAC,CAEhE;AAID,yCAAyC;AACzC,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAE9C;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAEhD;AAID,gEAAgE;AAChE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAErE;AAED,2DAA2D;AAC3D,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEpD;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEnD;AAID;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,4CAM7C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,4CAM7C"}
|
package/package.json
CHANGED
package/src/NativeSpotnySdk.ts
CHANGED
|
@@ -2,10 +2,15 @@ import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
|
2
2
|
|
|
3
3
|
export interface Spec extends TurboModule {
|
|
4
4
|
// ── Core scanning ──────────────────────────────────────────────────────────
|
|
5
|
-
startScanner(
|
|
5
|
+
startScanner(): Promise<string>;
|
|
6
6
|
stopScanner(): Promise<string>;
|
|
7
7
|
isScanning(): Promise<boolean>;
|
|
8
8
|
|
|
9
|
+
// ── Identity ───────────────────────────────────────────────────────────────
|
|
10
|
+
identify(userId: string): Promise<string>;
|
|
11
|
+
updateProfile(profile: Object): Promise<string>;
|
|
12
|
+
resetIdentity(): Promise<string>;
|
|
13
|
+
|
|
9
14
|
// ── Configuration ──────────────────────────────────────────────────────────
|
|
10
15
|
configure(config: Object): Promise<string>;
|
|
11
16
|
|
package/src/index.tsx
CHANGED
|
@@ -44,21 +44,23 @@ export type SpotnySdkConfig = {
|
|
|
44
44
|
distanceCorrectionFactor?: number;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
export type SpotnyProfile = {
|
|
48
|
+
/** Phone number (e.g. '+966501234567'). */
|
|
49
|
+
phoneNumber?: string;
|
|
50
|
+
/** Email address. */
|
|
51
|
+
email?: string;
|
|
52
|
+
/** Display name. */
|
|
53
|
+
name?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
47
56
|
// ── Internal event emitter ───────────────────────────────────────────────────
|
|
48
57
|
const eventEmitter = new NativeEventEmitter(NativeModules.SpotnySdk);
|
|
49
58
|
|
|
50
59
|
// ── Core scanning ────────────────────────────────────────────────────────────
|
|
51
60
|
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* @param userId Optional authenticated user ID.
|
|
56
|
-
*/
|
|
57
|
-
export function startScanner(
|
|
58
|
-
userUUID: string,
|
|
59
|
-
userId?: number | null
|
|
60
|
-
): Promise<string> {
|
|
61
|
-
return NativeSpotnySdk.startScanner(userUUID, userId ?? null);
|
|
61
|
+
/** Start beacon scanning. Call `identify()` before this for attributed tracking. */
|
|
62
|
+
export function startScanner(): Promise<string> {
|
|
63
|
+
return NativeSpotnySdk.startScanner();
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
/** Stop beacon scanning and clean up all state. */
|
|
@@ -71,6 +73,35 @@ export function isScanning(): Promise<boolean> {
|
|
|
71
73
|
return NativeSpotnySdk.isScanning();
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
// ── Identity ─────────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Identify the current user. Pass any opaque string your app uses — user ID,
|
|
80
|
+
* hashed email, hashed phone, etc. Stored in Keychain and sent with all
|
|
81
|
+
* subsequent tracking events.
|
|
82
|
+
*
|
|
83
|
+
* Call before `startScanner()` or at any point during an active session.
|
|
84
|
+
*/
|
|
85
|
+
export function identify(userId: string): Promise<string> {
|
|
86
|
+
return NativeSpotnySdk.identify(userId);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Update the user's profile on the Spotny backend.
|
|
91
|
+
* Fields are optional — pass only what you have.
|
|
92
|
+
*/
|
|
93
|
+
export function updateProfile(profile: SpotnyProfile): Promise<string> {
|
|
94
|
+
return NativeSpotnySdk.updateProfile(profile as Object);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Clear the stored identity (user ID + profile fields) from Keychain.
|
|
99
|
+
* Call on logout. The device ID is NOT affected.
|
|
100
|
+
*/
|
|
101
|
+
export function resetIdentity(): Promise<string> {
|
|
102
|
+
return NativeSpotnySdk.resetIdentity();
|
|
103
|
+
}
|
|
104
|
+
|
|
74
105
|
// ── Configuration ────────────────────────────────────────────────────────────
|
|
75
106
|
|
|
76
107
|
/**
|