@@ -6,6 +6,11 @@ import com.klaviyo.analytics.model.ProfileKey.ANONYMOUS_ID
6
6
import com.klaviyo.analytics.model.ProfileKey.EMAIL
7
7
import com.klaviyo.analytics.model.ProfileKey.EXTERNAL_ID
8
8
import com.klaviyo.analytics.model.ProfileKey.PHONE_NUMBER
9
+ import com.klaviyo.analytics.model.ProfileKey.PUSH_STATE
10
+ import com.klaviyo.analytics.model.ProfileKey.PUSH_TOKEN
11
+ import com.klaviyo.analytics.networking.ApiClient
12
+ import com.klaviyo.analytics.networking.requests.KlaviyoApiRequest.Status.Failed
13
+ import com.klaviyo.analytics.networking.requests.PushTokenApiRequest
9
14
import com.klaviyo.core.Registry
10
15
import java.util.UUID
11
16
@@ -14,6 +19,15 @@ import java.util.UUID
14
19
*/
15
20
internal object UserInfo {
16
21
22
+ init {
23
+ Registry .get<ApiClient >().onApiRequest { request ->
24
+ // If push token request totally fails, we must remove it from state
25
+ if (request is PushTokenApiRequest && request.status == Failed ) {
26
+ pushState = " "
27
+ }
28
+ }
29
+ }
30
+
17
31
/* *
18
32
* Save or clear an identifier in the persistent store and return it
19
33
*
@@ -56,9 +70,42 @@ internal object UserInfo {
56
70
*/
57
71
var anonymousId: String = " "
58
72
private set(value) { field = persist(ANONYMOUS_ID , value) }
59
- get() = field.ifEmpty { fetch(ANONYMOUS_ID , generateUuid).also { anonymousId = it } }
73
+ get() = field.ifEmpty { fetch(ANONYMOUS_ID , ::generateUuid).also { anonymousId = it } }
74
+
75
+ var pushToken: String = " "
76
+ private set(value) { field = persist(PUSH_TOKEN , value) }
77
+ get() = field.ifEmpty { fetch(PUSH_TOKEN ).also { field = it } }
60
78
61
- private val generateUuid = { UUID .randomUUID().toString() }
79
+ /* *
80
+ * Track the most recent state of push token + device metadata sent to the backend API
81
+ */
82
+ private var pushState: String = " "
83
+ set(value) { field = persist(PUSH_STATE , value) }
84
+ get() = field.ifEmpty { fetch(PUSH_STATE ).also { field = it } }
85
+
86
+ /* *
87
+ * Save push token string to state
88
+ * If push token or any other device metadata have changed,
89
+ * invoke the onChanged callback (i.e. to enqueue the API request)
90
+ */
91
+ fun setPushToken (token : String , onChanged : () -> Unit ) {
92
+ // Use the request body format as our state tracking value
93
+ val newPushState = PushTokenApiRequest (token, getAsProfile()).requestBody
94
+
95
+ if (newPushState != pushState) {
96
+ // Optimistic update algorithm: expect request to get to backend,
97
+ // on failure reset push state (see initializer). The main advantage to
98
+ // this algorithm is it prevents queueing duplicate requests immediately
99
+ pushState = newPushState ? : " "
100
+ pushToken = token
101
+ onChanged()
102
+ }
103
+ }
104
+
105
+ /* *
106
+ * Generate a new UUID for anonymous ID
107
+ */
108
+ private fun generateUuid () = UUID .randomUUID().toString()
62
109
63
110
/* *
64
111
* Indicate whether we currently have externally-set profile identifiers
@@ -75,6 +122,8 @@ internal object UserInfo {
75
122
email = " "
76
123
phoneNumber = " "
77
124
anonymousId = " "
125
+ pushToken = " "
126
+ pushState = " "
78
127
}
79
128
80
129
/* *
0 commit comments