Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Paywalls V2] TextComponentView handles overrides with TextComponentState #1989

Merged
merged 69 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
15ee579
Adds PaywallComponentsData to Offering, and has PaywallViewModel use …
JayShortway Dec 11, 2024
97a6d2a
Renames PaywallDataValidationTest to LegacyPaywallDataValidationTest.
JayShortway Dec 12, 2024
f039302
LoadingPaywall calls toLegacyPaywallState.
JayShortway Dec 12, 2024
9153fc0
OfferingParser parses paywall_components.
JayShortway Dec 12, 2024
0a07fd5
Adds Paywalls V2 support to PaywallsTester.
JayShortway Dec 12, 2024
8616a26
Adds missing circle MaskShape.
JayShortway Dec 12, 2024
bbe72eb
LoadedPaywallComponents will always fill the maximum available size.
JayShortway Dec 12, 2024
27837e2
ImageUrls width and height are optional.
JayShortway Dec 12, 2024
0e60bd3
Merge branch 'main' into pw2-offerings-data
JayShortway Dec 12, 2024
3d6fd64
Merge branch 'pw2-offerings-data' into pw2-tester
JayShortway Dec 12, 2024
da94c38
Reverts Constants.
JayShortway Dec 12, 2024
f849520
Merge branch 'pw2-tester' into pw2-various-fixes
JayShortway Dec 12, 2024
b669421
Fixes lint.
JayShortway Dec 12, 2024
07cfc1c
Merge branch 'pw2-offerings-data' into pw2-tester
JayShortway Dec 12, 2024
442b479
Merge branch 'pw2-tester' into pw2-various-fixes
JayShortway Dec 12, 2024
49e604a
StackComponentView changes background color when the theme changes.
JayShortway Dec 12, 2024
2b5574d
Adds a failing test to StackComponentViewTests.
JayShortway Dec 12, 2024
7271506
Adds 2 previews.
JayShortway Dec 12, 2024
dc58101
Ensures MDParagraph uses the correct fontSize.
JayShortway Dec 12, 2024
4f4bc41
Merge branch 'main' into pw2-offerings-data
JayShortway Dec 12, 2024
c7657da
Merge branch 'pw2-offerings-data' into pw2-tester
JayShortway Dec 12, 2024
4b54f33
Merge branch 'pw2-tester' into pw2-various-fixes
JayShortway Dec 12, 2024
831854a
Merge branch 'pw2-various-fixes' into pw2-fix-fontsize
JayShortway Dec 12, 2024
7bc2786
Adds another failing test to StackComponentViewTests.
JayShortway Dec 12, 2024
38401ed
Removing some redundant parameters.
JayShortway Dec 12, 2024
65affb8
Fixes the border test.
JayShortway Dec 12, 2024
453b5cf
Fixes the shadow test.
JayShortway Dec 12, 2024
4c189ee
Some cleanup.
JayShortway Dec 12, 2024
7952c6a
Merge branch 'pw2-fix-fontsize' into pw2-stack-tests
JayShortway Dec 12, 2024
a342ed8
Merge branch 'main' into pw2-tester
JayShortway Dec 13, 2024
2cc6c97
Merge branch 'pw2-tester' into pw2-various-fixes
JayShortway Dec 13, 2024
7cca77b
Merge branch 'pw2-various-fixes' into pw2-fix-fontsize
JayShortway Dec 13, 2024
c43525e
Merge branch 'pw2-fix-fontsize' into pw2-stack-tests
JayShortway Dec 13, 2024
914d9da
Adds a regression test.
JayShortway Dec 13, 2024
9abcb6a
Excludes Paywalls V1 from new font size behavior.
JayShortway Dec 13, 2024
870c577
Merge branch 'main' into pw2-fix-fontsize
JayShortway Dec 13, 2024
d5c4ae9
Merge branch 'pw2-fix-fontsize' into pw2-stack-tests
JayShortway Dec 13, 2024
43f1ecf
TextComponentStyle no longer needs a Composable context to be created.
JayShortway Dec 13, 2024
412312e
TextComponentStyle just has a single constructor now.
JayShortway Dec 13, 2024
9a6bab4
Merge branch 'main' into pw2-stack-tests
JayShortway Dec 16, 2024
8bcb10d
Moves rememberProcessedText from StyleFactory to TextComponentView.
JayShortway Dec 16, 2024
524ae3b
Removes unused StyleFactory parameters.
JayShortway Dec 16, 2024
7adc5d3
StyleFactory no longer needs a Composable context.
JayShortway Dec 16, 2024
d57b87a
Merge branch 'pw2-stack-tests' into pw2-simplify-textstyle
JayShortway Dec 16, 2024
7ef7d46
Locale and LocalizationDictionary are part of PaywallState now.
JayShortway Dec 16, 2024
ea77f24
Adds LoadedPaywallComponentsLocaleTests.
JayShortway Dec 16, 2024
8237323
Adds isEligibleForIntroOffer to PaywallState.
JayShortway Dec 16, 2024
68f3c38
Removes braces from a Preview annotation to please lint.
JayShortway Dec 16, 2024
f18ea32
Moves PackageContext to PaywallState.
JayShortway Dec 16, 2024
81bb7a6
Renames VariableContextTests to MostExpensivePricePerMonthMicrosTests.
JayShortway Dec 16, 2024
d7a1367
Moves MostExpensivePricePerMonthMicrosTests to the data package.
JayShortway Dec 16, 2024
dfc5024
TextComponentView uses PaywallState.
JayShortway Dec 16, 2024
f69c7b9
MostExpensivePricePerMonthMicrosTests uses FakePaywallState.
JayShortway Dec 16, 2024
2fdc87c
Merge branch 'main' into pw2-more-paywallstate
JayShortway Dec 17, 2024
5544518
Reorders imports in ButtonComponentView.
JayShortway Dec 17, 2024
cb25d3a
Adds TextComponentState.
JayShortway Dec 17, 2024
895f085
Adds PresentedOverrides to TextComponentStyle, and makes use of TextC…
JayShortway Dec 17, 2024
fb75780
Fixes ButtonComponentViewTests.
JayShortway Dec 17, 2024
7a7e944
Merge branch 'pw2-more-paywallstate' into pw2-component-state
JayShortway Dec 17, 2024
d1373ba
Fixes ButtonComponentViewTests once more.
JayShortway Dec 17, 2024
900a58d
Adds TextComponentViewWindowTests.
JayShortway Dec 17, 2024
0fe9f49
Adds windowChangingTest.
JayShortway Dec 17, 2024
77efa21
Adds WithoutActivityRecreationTests to TextComponentViewWindowTests.
JayShortway Dec 17, 2024
ce73645
Updates a comment.
JayShortway Dec 17, 2024
ececc0e
Adds a selected overrides test.
JayShortway Dec 17, 2024
c2bbc4f
Adds an intro offer overrides test.
JayShortway Dec 17, 2024
0d49f83
Fixes LoadedPaywallComponentsLocaleTests.
JayShortway Dec 17, 2024
7023710
Merge branch 'main' into pw2-component-state
JayShortway Dec 18, 2024
cc60503
Merge branch 'main' into pw2-component-state
JayShortway Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ private fun previewButtonComponentStyle(
size = Size(width = Fit, height = Fit),
padding = Padding(top = 8.0, bottom = 8.0, leading = 8.0, trailing = 8.0).toPaddingValues(),
margin = Padding(top = 0.0, bottom = 24.0, leading = 0.0, trailing = 24.0).toPaddingValues(),
overrides = null,
),
),
dimension = Dimension.Vertical(alignment = HorizontalAlignment.CENTER, distribution = START),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ private fun StackComponentView_Preview_ZLayer() {
size = Size(width = Fit, height = Fit),
padding = Padding(top = 8.0, bottom = 8.0, leading = 8.0, trailing = 8.0).toPaddingValues(),
margin = Padding(top = 0.0, bottom = 24.0, leading = 0.0, trailing = 24.0).toPaddingValues(),
overrides = null,
),
TextComponentStyle(
visible = true,
Expand All @@ -239,6 +240,7 @@ private fun StackComponentView_Preview_ZLayer() {
size = Size(width = Fit, height = Fit),
padding = Padding(top = 8.0, bottom = 8.0, leading = 8.0, trailing = 8.0).toPaddingValues(),
margin = Padding(top = 0.0, bottom = 0.0, leading = 0.0, trailing = 0.0).toPaddingValues(),
overrides = null,
),
),
dimension = Dimension.ZLayer(alignment = TwoDimensionalAlignment.BOTTOM_TRAILING),
Expand Down Expand Up @@ -283,6 +285,7 @@ private fun previewChildren() = listOf(
size = Size(width = Fit, height = Fit),
padding = Padding(top = 8.0, bottom = 8.0, leading = 8.0, trailing = 8.0).toPaddingValues(),
margin = Padding(top = 0.0, bottom = 0.0, leading = 0.0, trailing = 0.0).toPaddingValues(),
overrides = null,
),
TextComponentStyle(
visible = true,
Expand All @@ -301,6 +304,7 @@ private fun previewChildren() = listOf(
size = Size(width = Fit, height = Fit),
padding = Padding(top = 8.0, bottom = 8.0, leading = 8.0, trailing = 8.0).toPaddingValues(),
margin = Padding(top = 0.0, bottom = 0.0, leading = 0.0, trailing = 0.0).toPaddingValues(),
overrides = null,
),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import com.revenuecat.purchases.ui.revenuecatui.components.toPresentedOverrides
import com.revenuecat.purchases.ui.revenuecatui.errors.PaywallValidationError
import com.revenuecat.purchases.ui.revenuecatui.helpers.NonEmptyList
import com.revenuecat.purchases.ui.revenuecatui.helpers.Result
import com.revenuecat.purchases.ui.revenuecatui.helpers.map
import com.revenuecat.purchases.ui.revenuecatui.helpers.mapError
import com.revenuecat.purchases.ui.revenuecatui.helpers.mapOrAccumulate
import com.revenuecat.purchases.ui.revenuecatui.helpers.nonEmptyListOf
Expand Down Expand Up @@ -101,27 +100,23 @@ internal class StyleFactory(
// Map all overrides to PresentedOverrides.
?.toPresentedOverrides { LocalizedTextPartial(from = it, using = localizationDictionary) }
.orSuccessfullyNull()
// Pick a single PresentedPartial to show.
.map { it?.buildPresentedPartial(windowSize, isEligibleForIntroOffer, componentState) }
.mapError { nonEmptyListOf(it) },
) { text, presentedPartial ->
// Combine the text and PresentedPartial into a TextComponentStyle.
val partial = presentedPartial?.partial
val weight = (partial?.fontWeight ?: component.fontWeight).toFontWeight()

) { text, presentedOverrides ->
val weight = component.fontWeight.toFontWeight()
TextComponentStyle(
visible = partial?.visible ?: true,
text = presentedPartial?.text ?: text,
color = partial?.color ?: component.color,
fontSize = partial?.fontSize ?: component.fontSize,
visible = true,
text = text,
color = component.color,
fontSize = component.fontSize,
fontWeight = weight,
fontFamily = (partial?.fontName ?: component.fontName)?.let { SystemFontFamily(it, weight) },
textAlign = (partial?.horizontalAlignment ?: component.horizontalAlignment).toTextAlign(),
horizontalAlignment = (partial?.horizontalAlignment ?: component.horizontalAlignment).toAlignment(),
backgroundColor = partial?.backgroundColor ?: component.backgroundColor,
size = partial?.size ?: component.size,
padding = (partial?.padding ?: component.padding).toPaddingValues(),
margin = (partial?.margin ?: component.margin).toPaddingValues(),
fontFamily = component.fontName?.let { SystemFontFamily(it, weight) },
textAlign = component.horizontalAlignment.toTextAlign(),
horizontalAlignment = component.horizontalAlignment.toAlignment(),
backgroundColor = component.backgroundColor,
size = component.size,
padding = component.padding.toPaddingValues(),
margin = component.margin.toPaddingValues(),
overrides = presentedOverrides,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import androidx.compose.ui.text.style.TextAlign
import com.revenuecat.purchases.paywalls.components.properties.ColorScheme
import com.revenuecat.purchases.paywalls.components.properties.FontSize
import com.revenuecat.purchases.paywalls.components.properties.Size
import com.revenuecat.purchases.ui.revenuecatui.components.LocalizedTextPartial
import com.revenuecat.purchases.ui.revenuecatui.components.PresentedOverrides

@Suppress("LongParameterList")
@Immutable
Expand Down Expand Up @@ -37,4 +39,6 @@ internal class TextComponentStyle(
val padding: PaddingValues,
@get:JvmSynthetic
val margin: PaddingValues,
@get:JvmSynthetic
val overrides: PresentedOverrides<LocalizedTextPartial>?,
) : ComponentStyle
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
@file:JvmSynthetic

package com.revenuecat.purchases.ui.revenuecatui.components.text

import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.window.core.layout.WindowWidthSizeClass
import com.revenuecat.purchases.ui.revenuecatui.components.ComponentViewState
import com.revenuecat.purchases.ui.revenuecatui.components.ScreenCondition
import com.revenuecat.purchases.ui.revenuecatui.components.SystemFontFamily
import com.revenuecat.purchases.ui.revenuecatui.components.buildPresentedPartial
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toAlignment
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toFontWeight
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toPaddingValues
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toTextAlign
import com.revenuecat.purchases.ui.revenuecatui.components.style.TextComponentStyle
import com.revenuecat.purchases.ui.revenuecatui.data.PaywallState

@JvmSynthetic
@Composable
internal fun rememberUpdatedTextComponentState(
style: TextComponentStyle,
paywallState: PaywallState.Loaded.Components,
selected: Boolean,
): TextComponentState =
rememberUpdatedTextComponentState(
style = style,
isEligibleForIntroOffer = paywallState.isEligibleForIntroOffer,
selected = selected,
)

@JvmSynthetic
@Composable
internal fun rememberUpdatedTextComponentState(
style: TextComponentStyle,
isEligibleForIntroOffer: Boolean = false,
selected: Boolean = false,
): TextComponentState {
val windowSize = currentWindowAdaptiveInfo().windowSizeClass.windowWidthSizeClass

return remember(style) {
TextComponentState(
initialWindowSize = windowSize,
initialIsEligibleForIntroOffer = isEligibleForIntroOffer,
initialSelected = selected,
style = style,
)
}.apply {
update(
windowSize = windowSize,
isEligibleForIntroOffer = isEligibleForIntroOffer,
selected = selected,
)
}
}

@Stable
internal class TextComponentState(
initialWindowSize: WindowWidthSizeClass,
initialIsEligibleForIntroOffer: Boolean,
initialSelected: Boolean,
private val style: TextComponentStyle,
) {
private var windowSize by mutableStateOf(initialWindowSize)
private var isEligibleForIntroOffer by mutableStateOf(initialIsEligibleForIntroOffer)
private var selected by mutableStateOf(initialSelected)
private val presentedPartial by derivedStateOf {
val windowCondition = ScreenCondition.from(windowSize)
val componentState = if (selected) ComponentViewState.SELECTED else ComponentViewState.DEFAULT

style.overrides?.buildPresentedPartial(windowCondition, isEligibleForIntroOffer, componentState)
}

@get:JvmSynthetic
val visible by derivedStateOf { presentedPartial?.partial?.visible ?: true }

@get:JvmSynthetic
val text by derivedStateOf { presentedPartial?.text ?: style.text }

@get:JvmSynthetic
val color by derivedStateOf { presentedPartial?.partial?.color ?: style.color }

@get:JvmSynthetic
val fontSize by derivedStateOf { presentedPartial?.partial?.fontSize ?: style.fontSize }

@get:JvmSynthetic
val fontWeight by derivedStateOf { presentedPartial?.partial?.fontWeight?.toFontWeight() ?: style.fontWeight }

@get:JvmSynthetic
val fontFamily by derivedStateOf {
presentedPartial?.partial?.fontName?.let { SystemFontFamily(it, fontWeight) } ?: style.fontFamily
}

@get:JvmSynthetic
val textAlign by derivedStateOf {
presentedPartial?.partial?.horizontalAlignment?.toTextAlign() ?: style.textAlign
}

@get:JvmSynthetic
val horizontalAlignment by derivedStateOf {
presentedPartial?.partial?.horizontalAlignment?.toAlignment() ?: style.horizontalAlignment
}

@get:JvmSynthetic
val backgroundColor by derivedStateOf { presentedPartial?.partial?.backgroundColor ?: style.backgroundColor }

@get:JvmSynthetic
val size by derivedStateOf { presentedPartial?.partial?.size ?: style.size }

@get:JvmSynthetic
val padding by derivedStateOf { presentedPartial?.partial?.padding?.toPaddingValues() ?: style.padding }

@get:JvmSynthetic
val margin by derivedStateOf { presentedPartial?.partial?.margin?.toPaddingValues() ?: style.margin }

@JvmSynthetic
fun update(
windowSize: WindowWidthSizeClass? = null,
isEligibleForIntroOffer: Boolean? = null,
selected: Boolean? = null,
) {
if (windowSize != null) this.windowSize = windowSize
if (isEligibleForIntroOffer != null) this.isEligibleForIntroOffer = isEligibleForIntroOffer
if (selected != null) this.selected = selected
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,26 @@ internal fun TextComponentView(
style: TextComponentStyle,
state: PaywallState.Loaded.Components,
modifier: Modifier = Modifier,
selected: Boolean = false,
) {
// Get a TextComponentState that calculates the overridden properties we should use.
val textState = rememberUpdatedTextComponentState(
style = style,
paywallState = state,
selected = selected,
)

// Process any variables in the text.
val context = LocalContext.current
val variableDataProvider = remember { VariableDataProvider(context.toResourceProvider()) }
val text = rememberProcessedText(
originalText = style.text,
variables = variableDataProvider,
state = state,
textState = textState,
variables = variableDataProvider,
)

val colorStyle = rememberColorStyle(scheme = style.color)
val backgroundColorStyle = style.backgroundColor?.let { rememberColorStyle(scheme = it) }
val colorStyle = rememberColorStyle(scheme = textState.color)
val backgroundColorStyle = textState.backgroundColor?.let { rememberColorStyle(scheme = it) }

// Get the text color if it's solid.
val color = when (colorStyle) {
Expand All @@ -83,32 +92,32 @@ internal fun TextComponentView(
)
}

if (style.visible) {
if (textState.visible) {
Markdown(
text = text,
modifier = modifier
.size(style.size, horizontalAlignment = style.horizontalAlignment)
.padding(style.margin)
.size(textState.size, horizontalAlignment = textState.horizontalAlignment)
.padding(textState.margin)
.applyIfNotNull(backgroundColorStyle) { background(it) }
.padding(style.padding),
.padding(textState.padding),
color = color,
fontSize = style.fontSize.toTextUnit(),
fontWeight = style.fontWeight,
fontFamily = style.fontFamily,
horizontalAlignment = style.horizontalAlignment,
textAlign = style.textAlign,
fontSize = textState.fontSize.toTextUnit(),
fontWeight = textState.fontWeight,
fontFamily = textState.fontFamily,
horizontalAlignment = textState.horizontalAlignment,
textAlign = textState.textAlign,
style = textStyle,
)
}
}

@Composable
private fun rememberProcessedText(
originalText: String,
variables: VariableDataProvider,
state: PaywallState.Loaded.Components,
textState: TextComponentState,
variables: VariableDataProvider,
): String {
val processedText by remember(originalText) {
val processedText by remember(state, textState) {
derivedStateOf {
state.selectedPackage?.let { selectedPackage ->
val discount = discountPercentage(
Expand All @@ -122,11 +131,11 @@ private fun rememberProcessedText(
VariableProcessor.processVariables(
variableDataProvider = variables,
context = variableContext,
originalString = originalText,
originalString = textState.text,
rcPackage = selectedPackage,
locale = java.util.Locale.forLanguageTag(state.locale.toLanguageTag()),
)
} ?: originalText
} ?: textState.text
}
}

Expand Down Expand Up @@ -379,6 +388,7 @@ private fun previewTextComponentStyle(
size = size,
padding = padding.toPaddingValues(),
margin = margin.toPaddingValues(),
overrides = null,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class ButtonComponentViewTests {
padding = Padding(top = 8.0, bottom = 8.0, leading = 8.0, trailing = 8.0).toPaddingValues(),
margin = Padding(top = 0.0, bottom = 24.0, leading = 0.0, trailing = 24.0)
.toPaddingValues(),
overrides = null,
),
),
dimension = Dimension.Vertical(alignment = CENTER, distribution = START),
Expand Down
Loading