Skip to content

Commit 3cbac3d

Browse files
cursoragentjacob.rakidzich
authored andcommitted
Add PaywallDialogOptions.setWebViewMessageHandler
Thread the handler through to PaywallOptions when PaywallDialog builds internally. Handler participates in equals but is excluded from hashCode, mirroring PaywallOptions. Includes parity tests and api-tester coverage. Co-authored-by: jacob.rakidzich <jacob.rakidzich@revenuecat.com>
1 parent 8d10692 commit 3cbac3d

5 files changed

Lines changed: 93 additions & 0 deletions

File tree

api-tester/src/defaults/kotlin/com/revenuecat/apitester/kotlin/revenuecatui/PaywallDialogOptionsAPI.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ private class PaywallDialogOptionsAPI {
2424
.setListener(listener)
2525
.setShouldDisplayDismissButton(true)
2626
.setFontProvider(fontProvider)
27+
.setWebViewMessageHandler(null)
2728
.build()
2829
val shouldDisplayBlock2 = options.shouldDisplayBlock
2930
val offering2: Offering? = options.offering

ui/revenuecatui/api.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ package com.revenuecat.purchases.ui.revenuecatui {
4444
method public com.revenuecat.purchases.ui.revenuecatui.PaywallPurchaseLogic? getPurchaseLogic();
4545
method public kotlin.jvm.functions.Function1<com.revenuecat.purchases.CustomerInfo,java.lang.Boolean>? getShouldDisplayBlock();
4646
method public boolean getShouldDisplayDismissButton();
47+
method public com.revenuecat.purchases.ui.revenuecatui.PaywallWebViewMessageHandler? getWebViewMessageHandler();
4748
property public final java.util.Map<java.lang.String,com.revenuecat.purchases.ui.revenuecatui.CustomVariableValue> customVariables;
4849
property public final kotlin.jvm.functions.Function0<kotlin.Unit>? dismissRequest;
4950
property public final com.revenuecat.purchases.ui.revenuecatui.fonts.FontProvider? fontProvider;
@@ -52,6 +53,7 @@ package com.revenuecat.purchases.ui.revenuecatui {
5253
property public final com.revenuecat.purchases.ui.revenuecatui.PaywallPurchaseLogic? purchaseLogic;
5354
property public final kotlin.jvm.functions.Function1<com.revenuecat.purchases.CustomerInfo,java.lang.Boolean>? shouldDisplayBlock;
5455
property public final boolean shouldDisplayDismissButton;
56+
property public final com.revenuecat.purchases.ui.revenuecatui.PaywallWebViewMessageHandler? webViewMessageHandler;
5557
}
5658

5759
public static final class PaywallDialogOptions.Builder {
@@ -66,6 +68,7 @@ package com.revenuecat.purchases.ui.revenuecatui {
6668
method public com.revenuecat.purchases.ui.revenuecatui.PaywallDialogOptions.Builder setRequiredEntitlementIdentifier(String? requiredEntitlementIdentifier);
6769
method public com.revenuecat.purchases.ui.revenuecatui.PaywallDialogOptions.Builder setShouldDisplayBlock(kotlin.jvm.functions.Function1<? super com.revenuecat.purchases.CustomerInfo,java.lang.Boolean>? shouldDisplayBlock);
6870
method public com.revenuecat.purchases.ui.revenuecatui.PaywallDialogOptions.Builder setShouldDisplayDismissButton(boolean shouldDisplayDismissButton);
71+
method public com.revenuecat.purchases.ui.revenuecatui.PaywallDialogOptions.Builder setWebViewMessageHandler(com.revenuecat.purchases.ui.revenuecatui.PaywallWebViewMessageHandler? handler);
6972
}
7073

7174
public final class PaywallFooterKt {

ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/PaywallDialog.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,6 @@ private fun buildPaywallOptions(
132132
.setPurchaseLogic(paywallDialogOptions.purchaseLogic)
133133
.setDismissRequestWithExitOffering(dismissRequestWithExitOffering)
134134
.setCustomVariables(paywallDialogOptions.customVariables)
135+
.setWebViewMessageHandler(paywallDialogOptions.webViewMessageHandler)
135136
.build()
136137
}

ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/PaywallDialogOptions.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ public class PaywallDialogOptions internal constructor(
2222
* `{{ $custom.key }}` placeholders in the paywall configuration.
2323
*/
2424
public val customVariables: Map<String, CustomVariableValue> = emptyMap(),
25+
/**
26+
* Handler for messages sent by Paywalls V2 `web_view` components. See [PaywallWebViewMessageHandler].
27+
*/
28+
public val webViewMessageHandler: PaywallWebViewMessageHandler? = null,
2529
) {
2630

2731
internal val offeringSelection: OfferingSelection
@@ -36,8 +40,40 @@ public class PaywallDialogOptions internal constructor(
3640
listener = builder.listener,
3741
purchaseLogic = builder.purchaseLogic,
3842
customVariables = builder.customVariables,
43+
webViewMessageHandler = builder.webViewMessageHandler,
3944
)
4045

46+
public companion object {
47+
private const val hashMultiplier = 31
48+
}
49+
50+
// webViewMessageHandler is included in equals but excluded from hashCode (like PaywallOptions).
51+
override fun hashCode(): Int {
52+
var result = shouldDisplayDismissButton.hashCode()
53+
result = hashMultiplier * result + customVariables.hashCode()
54+
result = hashMultiplier * result + (offering?.identifier?.hashCode() ?: 0)
55+
return result
56+
}
57+
58+
override fun equals(other: Any?): Boolean {
59+
if (this === other) return true
60+
if (other !is PaywallDialogOptions) return false
61+
62+
return when {
63+
this.shouldDisplayBlock != other.shouldDisplayBlock -> false
64+
this.dismissRequest != other.dismissRequest -> false
65+
this.offering != other.offering -> false
66+
this.shouldDisplayDismissButton != other.shouldDisplayDismissButton -> false
67+
this.fontProvider != other.fontProvider -> false
68+
this.listener != other.listener -> false
69+
this.purchaseLogic != other.purchaseLogic -> false
70+
this.customVariables != other.customVariables -> false
71+
this.webViewMessageHandler != other.webViewMessageHandler -> false
72+
else -> true
73+
}
74+
}
75+
76+
@Suppress("TooManyFunctions")
4177
public class Builder {
4278
internal var shouldDisplayBlock: ((CustomerInfo) -> Boolean)? = null
4379
internal var dismissRequest: (() -> Unit)? = null
@@ -47,6 +83,7 @@ public class PaywallDialogOptions internal constructor(
4783
internal var listener: PaywallListener? = null
4884
internal var purchaseLogic: PaywallPurchaseLogic? = null
4985
internal var customVariables: Map<String, CustomVariableValue> = emptyMap()
86+
internal var webViewMessageHandler: PaywallWebViewMessageHandler? = null
5087

5188
/**
5289
* Allows to configure whether to display the paywall dialog depending on operations on the CustomerInfo
@@ -106,6 +143,15 @@ public class PaywallDialogOptions internal constructor(
106143
this.customVariables = variables
107144
}
108145

146+
/**
147+
* Sets a handler for messages sent by Paywalls V2 `web_view` components. The handler receives
148+
* validated messages (such as `rc:step-complete`, `rc:request-variables`, and `rc:error`) on
149+
* the main thread, along with a [PaywallWebViewController] for replying to the web view.
150+
*/
151+
public fun setWebViewMessageHandler(handler: PaywallWebViewMessageHandler?): Builder = apply {
152+
this.webViewMessageHandler = handler
153+
}
154+
109155
public fun build(): PaywallDialogOptions {
110156
return PaywallDialogOptions(this)
111157
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.revenuecat.purchases.ui.revenuecatui
2+
3+
import org.assertj.core.api.Assertions.assertThat
4+
import org.junit.Test
5+
6+
internal class PaywallDialogOptionsWebViewHandlerTest {
7+
8+
private fun options(handler: PaywallWebViewMessageHandler?): PaywallDialogOptions =
9+
PaywallDialogOptions.Builder()
10+
.setWebViewMessageHandler(handler)
11+
.build()
12+
13+
@Test
14+
fun `builder stores the web view message handler`() {
15+
val handler = PaywallWebViewMessageHandler { _, _ -> }
16+
17+
assertThat(options(handler).webViewMessageHandler).isSameAs(handler)
18+
}
19+
20+
@Test
21+
fun `defaults to no web view message handler`() {
22+
val options = PaywallDialogOptions.Builder().build()
23+
24+
assertThat(options.webViewMessageHandler).isNull()
25+
}
26+
27+
@Test
28+
fun `options differing only by handler are not equal`() {
29+
val handlerA = PaywallWebViewMessageHandler { _, _ -> }
30+
val handlerB = PaywallWebViewMessageHandler { _, _ -> }
31+
32+
assertThat(options(handlerA)).isNotEqualTo(options(handlerB))
33+
}
34+
35+
@Test
36+
fun `handler is excluded from hashCode so state updates are not triggered by it alone`() {
37+
val handlerA = PaywallWebViewMessageHandler { _, _ -> }
38+
val handlerB = PaywallWebViewMessageHandler { _, _ -> }
39+
40+
assertThat(options(handlerA).hashCode()).isEqualTo(options(handlerB).hashCode())
41+
}
42+
}

0 commit comments

Comments
 (0)