Skip to content

Commit

Permalink
events so far
Browse files Browse the repository at this point in the history
  • Loading branch information
simond-stripe committed Jan 8, 2025
1 parent 162d768 commit 9c48437
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal sealed class ConnectAnalyticsEvent(
* Note: This should happen before component_loaded, so we won't yet have a page_view_id.
*/
data class WebPageLoaded(
val timeToLoad: Double
val timeToLoad: Long
) : ConnectAnalyticsEvent(
"component.web.page_loaded",
mapOf("time_to_load" to timeToLoad.toString())
Expand All @@ -43,8 +43,8 @@ internal sealed class ConnectAnalyticsEvent(
*/
data class WebComponentLoaded(
val pageViewId: String,
val timeToLoad: Double,
val perceivedTimeToLoad: Double
val timeToLoad: Long,
val perceivedTimeToLoad: Long
) : ConnectAnalyticsEvent(
"component.web.component_loaded",
mapOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.stripe.android.connect.util

/**
* [Clock] interface to be used to provide compatible `Clock` functionality,
* and one day be replaced by `java.time.Clock` when all consumers support > SDK 26.
*
* Also useful for mocking in tests.
*/
interface Clock {

/**
* Return the current system time in milliseconds
*/
fun millis(): Long
}

/**
* A [Clock] that depends on Android APIs. To be replaced by java.time.Clock when all consumers
* support > SDK 26.
*/
class AndroidClock : Clock {
override fun millis(): Long = System.currentTimeMillis()
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.stripe.android.connect.StripeEmbeddedComponentListener
import com.stripe.android.connect.appearance.Appearance
import com.stripe.android.connect.databinding.StripeConnectWebviewBinding
import com.stripe.android.connect.toJsonObject
import com.stripe.android.connect.util.AndroidClock
import com.stripe.android.connect.webview.serialization.AccountSessionClaimedMessage
import com.stripe.android.connect.webview.serialization.ConnectInstanceJs
import com.stripe.android.connect.webview.serialization.ConnectJson
Expand Down Expand Up @@ -200,6 +201,7 @@ internal class StripeConnectWebViewContainerImpl<Listener, Props>(
this.controller = StripeConnectWebViewContainerController(
view = this,
analyticsService = analyticsService,
clock = AndroidClock(),
embeddedComponentManager = embeddedComponentManager,
embeddedComponent = embeddedComponent,
listener = listener,
Expand Down Expand Up @@ -267,6 +269,10 @@ internal class StripeConnectWebViewContainerImpl<Listener, Props>(
controller?.onPageStarted()
}

override fun onPageFinished(view: WebView?, url: String?) {
controller?.onPageFinished()
}

override fun onReceivedHttpError(
view: WebView,
request: WebResourceRequest,
Expand Down Expand Up @@ -368,7 +374,7 @@ internal class StripeConnectWebViewContainerImpl<Listener, Props>(
val pageLoadMessage = ConnectJson.decodeFromString<PageLoadMessage>(message)
logger.debug("Page did load: $pageLoadMessage")

controller?.onReceivedPageDidLoad()
controller?.onReceivedPageDidLoad(pageLoadMessage.pageViewId)
}

@JavascriptInterface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ import com.stripe.android.connect.PrivateBetaConnectSDK
import com.stripe.android.connect.StripeEmbeddedComponent
import com.stripe.android.connect.StripeEmbeddedComponentListener
import com.stripe.android.connect.analytics.ComponentAnalyticsService
import com.stripe.android.connect.analytics.ConnectAnalyticsEvent
import com.stripe.android.connect.util.Clock
import com.stripe.android.connect.webview.serialization.ConnectInstanceJs
import com.stripe.android.connect.webview.serialization.SetOnLoadError
import com.stripe.android.connect.webview.serialization.SetOnLoaderStart
import com.stripe.android.connect.webview.serialization.SetterFunctionCalledMessage
import com.stripe.android.connect.webview.serialization.SetterFunctionCalledMessage.UnknownValue
import com.stripe.android.core.Logger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -33,6 +36,7 @@ import kotlinx.coroutines.withContext
internal class StripeConnectWebViewContainerController<Listener : StripeEmbeddedComponentListener>(
private val view: StripeConnectWebViewContainerInternal,
private val analyticsService: ComponentAnalyticsService,
private val clock: Clock,
private val embeddedComponentManager: EmbeddedComponentManager,
private val embeddedComponent: StripeEmbeddedComponent,
private val listener: Listener?,
Expand All @@ -41,6 +45,10 @@ internal class StripeConnectWebViewContainerController<Listener : StripeEmbedded
private val logger: Logger = Logger.getInstance(enableLogging = BuildConfig.DEBUG),
) : DefaultLifecycleObserver {

init {
analyticsService.track(ConnectAnalyticsEvent.ComponentCreated)
}

private val loggerTag = javaClass.simpleName
private val _stateFlow = MutableStateFlow(StripeConnectWebViewContainerState())

Expand All @@ -54,7 +62,10 @@ internal class StripeConnectWebViewContainerController<Listener : StripeEmbedded
* Callback to invoke when the view is attached.
*/
fun onViewAttached() {
updateState { copy(didBeginLoadingMillis = clock.millis()) }
view.loadUrl(embeddedComponentManager.getStripeURL(embeddedComponent))

analyticsService.track(ConnectAnalyticsEvent.ComponentViewed(stateFlow.value.pageViewId))
}

/**
Expand All @@ -64,6 +75,14 @@ internal class StripeConnectWebViewContainerController<Listener : StripeEmbedded
updateState { copy(isNativeLoadingIndicatorVisible = !receivedSetOnLoaderStart) }
}

/**
* Callback to invoke when the page finished loading.
*/
fun onPageFinished() {
val timeToLoad = clock.millis() - (stateFlow.value.didBeginLoadingMillis ?: 0)
analyticsService.track(ConnectAnalyticsEvent.WebPageLoaded(timeToLoad))
}

/**
* Callback to invoke when the webview received a network error. If the error was an HTTP error,
* [httpStatusCode] will be non-null.
Expand All @@ -89,6 +108,11 @@ internal class StripeConnectWebViewContainerController<Listener : StripeEmbedded
// don't send errors for requests that aren't for the main page load
if (isMainPageLoad) {
listener?.onLoadError(RuntimeException(errorString)) // TODO - wrap error better
analyticsService.track(ConnectAnalyticsEvent.WebErrorPageLoad(
status = httpStatusCode,
error = errorMessage,
url = requestUrl
))
}
}

Expand Down Expand Up @@ -136,7 +160,7 @@ internal class StripeConnectWebViewContainerController<Listener : StripeEmbedded
embeddedComponentManager.appearanceFlow
.collectLatest { appearance ->
updateState { copy(appearance = appearance) }
if (stateFlow.value.receivedPageDidLoad) {
if (stateFlow.value.pageViewId != null) {
view.updateConnectInstance(appearance)
}
}
Expand Down Expand Up @@ -198,9 +222,16 @@ internal class StripeConnectWebViewContainerController<Listener : StripeEmbedded
/**
* Callback to invoke upon receiving 'pageDidLoad' message.
*/
fun onReceivedPageDidLoad() {
fun onReceivedPageDidLoad(pageViewId: String) {
view.updateConnectInstance(embeddedComponentManager.appearanceFlow.value)
updateState { copy(receivedPageDidLoad = true) }
updateState { copy(pageViewId = pageViewId) }

val timeToLoad = clock.millis() - (stateFlow.value.didBeginLoadingMillis ?: 0)
analyticsService.track(ConnectAnalyticsEvent.WebComponentLoaded(
pageViewId = pageViewId,
timeToLoad = timeToLoad, // right now view onAttach and begin load happen at the same time,
perceivedTimeToLoad = timeToLoad, // so timeToLoad and perceivedTimeToLoad are the same value
))
}

/**
Expand All @@ -222,6 +253,12 @@ internal class StripeConnectWebViewContainerController<Listener : StripeEmbedded
listener?.onLoadError(RuntimeException("${value.error.type}: ${value.error.message}"))
}
else -> {
if (value is UnknownValue) {
analyticsService.track(ConnectAnalyticsEvent.WebWarnUnrecognizedSetter(
setter = message.setter,
pageViewId = stateFlow.value.pageViewId
))
}
with(listenerDelegate) {
listener?.delegate(message)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import com.stripe.android.connect.util.getContrastingColor
@OptIn(PrivateBetaConnectSDK::class)
internal data class StripeConnectWebViewContainerState(
/**
* True if we received the 'pageDidLoad' message.
* Non-null if we received the 'pageDidLoad' message,
* null otherwise.
*/
val receivedPageDidLoad: Boolean = false,
val pageViewId: String? = null,

/**
* The time the webview began loading, in milliseconds from midnight, January 1, 1970 UTC.
*/
val didBeginLoadingMillis: Long? = null,

/**
* True if we received the 'setOnLoaderStart' message.
Expand Down

0 comments on commit 9c48437

Please sign in to comment.