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] Implements color aliases for backgrounds #2086

Merged
merged 35 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a0b0911
Fixes compilation of IconComponentView.
JayShortway Jan 22, 2025
fbb2cb6
ColorInfo.Alias has a ColorAlias value now.
JayShortway Jan 22, 2025
6357b73
Adds ColorStyles and a way to look them up.
JayShortway Jan 22, 2025
c0c801a
Adds UiConfig to StyleFactory and uses it to build StackComponentStyle.
JayShortway Jan 22, 2025
3dd6bf8
Adds PresentedStackPartialTests and a fix to ColorStyle.
JayShortway Jan 22, 2025
9946cc8
Adds ColorStyleTests
JayShortway Jan 22, 2025
ee91fd8
Merge branch 'main' into pw2-colorstyles
JayShortway Jan 22, 2025
d3e81c2
Updates documentation for a function.
JayShortway Jan 23, 2025
b148227
Renames a parameter.
JayShortway Jan 23, 2025
ab6a491
Fixes a compilation error.
JayShortway Jan 23, 2025
044b221
Fixes test compilation.
JayShortway Jan 23, 2025
8aeedbb
Adds ColorStyles to IconComponentStyle.
JayShortway Jan 23, 2025
ba778c0
Adds ColorStyles to IconComponentStyle.Background.
JayShortway Jan 23, 2025
0b36305
Adds PresentedIconPartialTests.
JayShortway Jan 23, 2025
fe11b77
Merge branch 'main' into pw2-colorstyles-icon
JayShortway Jan 23, 2025
f21353d
ImageComponentStyle.overlay is a ColorStyles now.
JayShortway Jan 23, 2025
240f508
Adds PresentedImagePartialTests.
JayShortway Jan 23, 2025
8e814c9
Changes ColorScheme properties to ColorStyles in TextComponentStyle.
JayShortway Jan 23, 2025
a733f8b
Adds more LocalizedTextPartialTests.
JayShortway Jan 23, 2025
89c5286
Introduces BorderStyles and adds it to StackComponentStyle.
JayShortway Jan 24, 2025
0b25b3b
Adds BorderStyles to IconComponentStyle.Background.
JayShortway Jan 24, 2025
7be4c38
Adds BorderStyles to ImageComponentStyle.
JayShortway Jan 24, 2025
7ab352b
Introduces ShadowStyles and adds it to StackComponentStyle.
JayShortway Jan 24, 2025
a508cf5
Adds ShadowStyles to IconComponentStyle.Background.
JayShortway Jan 24, 2025
da30ef7
Adds ShadowStyles to ImageComponentStyle.
JayShortway Jan 24, 2025
56e6b80
Adds BackgroundStyles and uses it for the main paywall.
JayShortway Jan 24, 2025
8b20983
Merge branch 'main' into pw2-colorstyles-icon
JayShortway Jan 24, 2025
27dd90f
Merge branch 'pw2-colorstyles-icon' into pw2-colorstyles-image
JayShortway Jan 24, 2025
199816c
Merge branch 'pw2-colorstyles-image' into pw2-colorstyles-text
JayShortway Jan 24, 2025
5300158
Merge branch 'pw2-colorstyles-text' into pw2-colorstyles-border
JayShortway Jan 24, 2025
967b4f0
Merge branch 'pw2-colorstyles-border' into pw2-colorstyles-shadow
JayShortway Jan 24, 2025
5891761
Merge branch 'pw2-colorstyles-shadow' into pw2-colorstyles-background
JayShortway Jan 24, 2025
44d9af5
Merge branch 'main' into pw2-colorstyles-background
JayShortway Jan 27, 2025
11eb74a
Re-reverts an unintended change.
JayShortway Jan 27, 2025
ae87952
Merge branch 'main' into pw2-colorstyles-background
JayShortway Jan 27, 2025
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 @@ -45,7 +45,7 @@ import com.revenuecat.purchases.paywalls.components.properties.SizeConstraint.Fi
import com.revenuecat.purchases.paywalls.components.properties.TwoDimensionalAlignment
import com.revenuecat.purchases.paywalls.components.properties.TwoDimensionalAlignment.BOTTOM
import com.revenuecat.purchases.ui.revenuecatui.components.modifier.background
import com.revenuecat.purchases.ui.revenuecatui.components.properties.toBackgroundStyle
import com.revenuecat.purchases.ui.revenuecatui.components.properties.rememberBackgroundStyle
import com.revenuecat.purchases.ui.revenuecatui.data.PaywallState
import com.revenuecat.purchases.ui.revenuecatui.helpers.getOrThrow
import com.revenuecat.purchases.ui.revenuecatui.helpers.toComponentsPaywallState
Expand All @@ -63,7 +63,7 @@ internal fun LoadedPaywallComponents(

val style = state.stack
val footerComponentStyle = state.stickyFooter
val background = state.background.toBackgroundStyle()
val background = rememberBackgroundStyle(state.background)

Column(modifier = modifier.background(background)) {
ComponentView(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,30 @@ package com.revenuecat.purchases.ui.revenuecatui.components.properties
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImagePainter
import coil.compose.rememberAsyncImagePainter
import com.revenuecat.purchases.ColorAlias
import com.revenuecat.purchases.paywalls.components.common.Background
import com.revenuecat.purchases.paywalls.components.properties.ColorInfo
import com.revenuecat.purchases.paywalls.components.properties.ColorScheme
import com.revenuecat.purchases.paywalls.components.properties.ImageUrls
import com.revenuecat.purchases.paywalls.components.properties.ThemeImageUrls
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toContentScale
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.urlsForCurrentTheme
import com.revenuecat.purchases.ui.revenuecatui.components.modifier.background
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.orSuccessfullyNull

/**
* Ready to use background properties for the current theme.
Expand All @@ -32,6 +42,67 @@ internal sealed interface BackgroundStyle {
) : BackgroundStyle
}

/**
* Background properties with resolved colors.
*/
internal sealed interface BackgroundStyles {
@JvmInline
value class Color(@get:JvmSynthetic val color: ColorStyles) : BackgroundStyles

data class Image(
@get:JvmSynthetic val sources: ThemeImageUrls,
@get:JvmSynthetic val contentScale: ContentScale,
@get:JvmSynthetic val colorOverlay: ColorStyles?,
) : BackgroundStyles
}

@JvmSynthetic
internal fun Background.toBackgroundStyles(
aliases: Map<ColorAlias, ColorScheme>,
): Result<BackgroundStyles, NonEmptyList<PaywallValidationError>> =
when (this) {
is Background.Color ->
value
.toColorStyles(aliases = aliases)
.map { color -> BackgroundStyles.Color(color) }

is Background.Image ->
colorOverlay
?.toColorStyles(aliases = aliases)
.orSuccessfullyNull()
.map { colorOverlay ->
BackgroundStyles.Image(
sources = value,
contentScale = fitMode.toContentScale(),
colorOverlay = colorOverlay,
)
}
}

@Composable
@JvmSynthetic
internal fun rememberBackgroundStyle(background: BackgroundStyles): BackgroundStyle =
when (background) {
is BackgroundStyles.Color -> {
val color = background.color.forCurrentTheme
remember(background, color) {
BackgroundStyle.Color(color = color)
}
}
is BackgroundStyles.Image -> {
val colorOverlay = background.colorOverlay?.forCurrentTheme
val source = background.sources.urlsForCurrentTheme
val painter = rememberAsyncImagePainter(source, background.contentScale)
remember(colorOverlay, source, painter) {
BackgroundStyle.Image(
painter = painter,
contentScale = background.contentScale,
colorOverlay = colorOverlay,
)
}
}
}

@JvmSynthetic
@Composable
internal fun Background.toBackgroundStyle(): BackgroundStyle =
Expand Down Expand Up @@ -59,6 +130,21 @@ internal fun Background.toBackgroundStyle(): BackgroundStyle =
}
}

@Composable
private fun rememberAsyncImagePainter(imageUrls: ImageUrls, contentScale: ContentScale): AsyncImagePainter =
rememberAsyncImagePainter(
model = imageUrls.webp.toString(),
placeholder = rememberAsyncImagePainter(
model = imageUrls.webpLowRes.toString(),
error = null,
fallback = null,
contentScale = contentScale,
),
error = null,
fallback = null,
contentScale = contentScale,
)

@Preview
@Composable
private fun Background_Preview_ColorHex() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.text.intl.LocaleList
import com.revenuecat.purchases.Offering
import com.revenuecat.purchases.Package
import com.revenuecat.purchases.paywalls.components.common.Background
import com.revenuecat.purchases.paywalls.components.common.LocaleId
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toComposeLocale
import com.revenuecat.purchases.ui.revenuecatui.components.ktx.toLocaleId
import com.revenuecat.purchases.ui.revenuecatui.components.properties.BackgroundStyles
import com.revenuecat.purchases.ui.revenuecatui.components.style.ComponentStyle
import com.revenuecat.purchases.ui.revenuecatui.data.processed.ProcessedLocalizedConfiguration
import com.revenuecat.purchases.ui.revenuecatui.data.processed.TemplateConfiguration
Expand Down Expand Up @@ -66,7 +66,7 @@ internal sealed interface PaywallState {
class Components(
val stack: ComponentStyle,
val stickyFooter: ComponentStyle?,
val background: Background,
val background: BackgroundStyles,
/**
* Some currencies do not commonly use decimals when displaying prices. Set this to false to accommodate
* for that.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.revenuecat.purchases.paywalls.components.common.LocalizationData
import com.revenuecat.purchases.paywalls.components.common.LocalizationKey
import com.revenuecat.purchases.paywalls.components.common.PaywallComponentsData
import com.revenuecat.purchases.ui.revenuecatui.PaywallMode
import com.revenuecat.purchases.ui.revenuecatui.components.properties.toBackgroundStyles
import com.revenuecat.purchases.ui.revenuecatui.components.style.StyleFactory
import com.revenuecat.purchases.ui.revenuecatui.composables.PaywallIconName
import com.revenuecat.purchases.ui.revenuecatui.data.PaywallState
Expand Down Expand Up @@ -116,15 +117,16 @@ internal fun Offering.validatePaywallComponentsDataOrNull(): RcResult<PaywallVal
)
val config = paywallComponents.data.componentsConfig.base

// Combine the main stack with the stickyFooter, or accumulate the encountered errors.
// Combine the main stack with the stickyFooter and the background, or accumulate the encountered errors.
return zipOrAccumulate(
styleFactory.create(config.stack),
config.stickyFooter?.let { styleFactory.create(it) }.orSuccessfullyNull(),
) { stack, stickyFooter ->
first = styleFactory.create(config.stack),
second = config.stickyFooter?.let { styleFactory.create(it) }.orSuccessfullyNull(),
third = config.background.toBackgroundStyles(aliases = paywallComponents.uiConfig.app.colors),
) { stack, stickyFooter, background ->
PaywallValidationResult.Components(
stack = stack,
stickyFooter = stickyFooter,
background = config.background,
background = background,
locales = localizations.keys,
zeroDecimalPlaceCountries = paywallComponents.data.zeroDecimalPlaceCountries.toSet(),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.revenuecat.purchases.ui.revenuecatui.helpers

import com.revenuecat.purchases.paywalls.PaywallData
import com.revenuecat.purchases.paywalls.components.common.Background
import com.revenuecat.purchases.paywalls.components.common.LocaleId
import com.revenuecat.purchases.ui.revenuecatui.components.properties.BackgroundStyles
import com.revenuecat.purchases.ui.revenuecatui.components.style.ComponentStyle
import com.revenuecat.purchases.ui.revenuecatui.data.processed.PaywallTemplate
import com.revenuecat.purchases.ui.revenuecatui.errors.PaywallValidationError
Expand All @@ -29,7 +29,7 @@ internal sealed interface PaywallValidationResult {
data class Components(
val stack: ComponentStyle,
val stickyFooter: ComponentStyle?,
val background: Background,
val background: BackgroundStyles,
/**
* All locales that this paywall supports, with `locales.head` being the default one.
*/
Expand Down