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

Add PlaygroundConfigurationData for configuring known playground operations. #8444

Merged
merged 2 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -37,8 +37,7 @@ import com.stripe.android.paymentsheet.example.playground.settings.CheckoutMode
import com.stripe.android.paymentsheet.example.playground.settings.CheckoutModeSettingsDefinition
import com.stripe.android.paymentsheet.example.playground.settings.CustomerSettingsDefinition
import com.stripe.android.paymentsheet.example.playground.settings.CustomerType
import com.stripe.android.paymentsheet.example.playground.settings.IntegrationType
import com.stripe.android.paymentsheet.example.playground.settings.IntegrationTypeSettingsDefinition
import com.stripe.android.paymentsheet.example.playground.settings.PlaygroundConfigurationData
import com.stripe.android.paymentsheet.ui.PAYMENT_SHEET_ERROR_TEXT_TEST_TAG
import com.stripe.android.test.core.ui.BrowserUI
import com.stripe.android.test.core.ui.ComposeButton
Expand Down Expand Up @@ -181,7 +180,11 @@ internal class PlaygroundTestDriver(
): PlaygroundState? {
setup(
testParameters.copyPlaygroundSettings { settings ->
settings[IntegrationTypeSettingsDefinition] = IntegrationType.FlowController
settings.updateConfigurationData { configurationData ->
configurationData.copy(
integrationType = PlaygroundConfigurationData.IntegrationType.FlowController
)
}
}
)
launchCustom()
Expand Down Expand Up @@ -229,7 +232,11 @@ internal class PlaygroundTestDriver(
): PlaygroundState? {
setup(
testParameters.copyPlaygroundSettings { settings ->
settings[IntegrationTypeSettingsDefinition] = IntegrationType.FlowController
settings.updateConfigurationData { configurationData ->
configurationData.copy(
integrationType = PlaygroundConfigurationData.IntegrationType.FlowController
)
}

customerId?.let { id ->
settings[CustomerSettingsDefinition] = CustomerType.Existing(id)
Expand Down Expand Up @@ -283,7 +290,12 @@ internal class PlaygroundTestDriver(

setup(
testParameters.copyPlaygroundSettings { settings ->
settings[IntegrationTypeSettingsDefinition] = IntegrationType.FlowController
settings.updateConfigurationData { configurationData ->
configurationData.copy(
integrationType = PlaygroundConfigurationData.IntegrationType.FlowController
)
}

settings[CustomerSettingsDefinition] = CustomerType.Existing(customerId)
}
)
Expand Down Expand Up @@ -605,7 +617,11 @@ internal class PlaygroundTestDriver(
) {
setup(
testParameters.copyPlaygroundSettings { settings ->
settings[IntegrationTypeSettingsDefinition] = IntegrationType.FlowController
settings.updateConfigurationData { configurationData ->
configurationData.copy(
integrationType = PlaygroundConfigurationData.IntegrationType.FlowController
)
}
}
)
launchCustom()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import com.stripe.android.paymentsheet.example.playground.activity.FawryActivity
import com.stripe.android.paymentsheet.example.playground.activity.QrCodeActivity
import com.stripe.android.paymentsheet.example.playground.settings.CheckoutMode
import com.stripe.android.paymentsheet.example.playground.settings.InitializationType
import com.stripe.android.paymentsheet.example.playground.settings.IntegrationType
import com.stripe.android.paymentsheet.example.playground.settings.PlaygroundConfigurationData
import com.stripe.android.paymentsheet.example.playground.settings.PlaygroundSettings
import com.stripe.android.paymentsheet.example.playground.settings.SettingsUi
import com.stripe.android.paymentsheet.example.samples.ui.shared.BuyButton
Expand Down Expand Up @@ -223,14 +223,14 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
)

when (playgroundState.integrationType) {
IntegrationType.PaymentSheet -> {
PlaygroundConfigurationData.IntegrationType.PaymentSheet -> {
PaymentSheetUi(
paymentSheet = paymentSheet,
playgroundState = playgroundState,
)
}

IntegrationType.FlowController -> {
PlaygroundConfigurationData.IntegrationType.FlowController -> {
FlowControllerUi(
flowController = flowController,
playgroundState = playgroundState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.stripe.android.paymentsheet.example.playground.settings.CheckoutModeS
import com.stripe.android.paymentsheet.example.playground.settings.CountrySettingsDefinition
import com.stripe.android.paymentsheet.example.playground.settings.CurrencySettingsDefinition
import com.stripe.android.paymentsheet.example.playground.settings.InitializationTypeSettingsDefinition
import com.stripe.android.paymentsheet.example.playground.settings.IntegrationTypeSettingsDefinition
import com.stripe.android.paymentsheet.example.playground.settings.PaymentMethodConfigurationSettingsDefinition
import com.stripe.android.paymentsheet.example.playground.settings.PlaygroundSettings

Expand All @@ -24,7 +23,7 @@ internal data class PlaygroundState(
val currencyCode = snapshot[CurrencySettingsDefinition]
val countryCode = snapshot[CountrySettingsDefinition]
val checkoutMode = snapshot[CheckoutModeSettingsDefinition]
val integrationType = snapshot[IntegrationTypeSettingsDefinition]
val integrationType = snapshot.configurationData.integrationType
val paymentMethodConfigurationId: String? = snapshot[PaymentMethodConfigurationSettingsDefinition].ifEmpty { null }

val stripeIntentId: String
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.stripe.android.paymentsheet.example.playground.settings

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class PlaygroundConfigurationData(
val integrationType: IntegrationType = IntegrationType.PaymentSheet,
) {
@Serializable
enum class IntegrationType {
@SerialName("paymentSheet")
PaymentSheet,

@SerialName("flowController")
FlowController,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ internal interface PlaygroundSettingDefinition<T> {

fun valueUpdated(value: T, playgroundSettings: PlaygroundSettings) {}

fun applicable(configurationData: PlaygroundConfigurationData): Boolean = true

fun saveable(): Saveable<T>? {
@Suppress("UNCHECKED_CAST")
return this as? Saveable<T>?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
package com.stripe.android.paymentsheet.example.playground.settings

import android.content.Context
import android.util.Log
import androidx.compose.runtime.Stable
import androidx.core.content.edit
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.example.playground.PlaygroundState
import com.stripe.android.paymentsheet.example.playground.model.CheckoutRequest
import com.stripe.android.uicore.utils.mapAsStateFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive

internal class PlaygroundSettings private constructor(
initialConfigurationData: PlaygroundConfigurationData,
private val settings: MutableMap<PlaygroundSettingDefinition<*>, MutableStateFlow<Any?>>
) {
private val _configurationData = MutableStateFlow(initialConfigurationData)
val configurationData = _configurationData.asStateFlow()

val displayableDefinitions = _configurationData.mapAsStateFlow { data ->
settings
.filterKeys { it.applicable(data) }
.map { (definition, _) -> definition }
.filterIsInstance<PlaygroundSettingDefinition.Displayable<*>>()
}

operator fun <T> get(settingsDefinition: PlaygroundSettingDefinition<T>): StateFlow<T> {
@Suppress("UNCHECKED_CAST")
return settings[settingsDefinition]?.asStateFlow() as StateFlow<T>
Expand All @@ -31,15 +44,23 @@ internal class PlaygroundSettings private constructor(
settingsDefinition.valueUpdated(value, this)
}

fun updateConfigurationData(
updater: (PlaygroundConfigurationData) -> PlaygroundConfigurationData
) {
_configurationData.value = updater(_configurationData.value)
}

fun snapshot(): Snapshot {
return Snapshot(this)
}

@Stable
class Snapshot private constructor(
val configurationData: PlaygroundConfigurationData,
private val settings: Map<PlaygroundSettingDefinition<*>, Any?>
) {
constructor(playgroundSettings: PlaygroundSettings) : this(
playgroundSettings.configurationData.value,
playgroundSettings.settings.map { it.key to it.value.value }.toMap()
)

Expand All @@ -52,17 +73,19 @@ internal class PlaygroundSettings private constructor(
val mutableSettings = settings.map {
it.key to MutableStateFlow(it.value)
}.toMap().toMutableMap()
return PlaygroundSettings(mutableSettings)
return PlaygroundSettings(configurationData, mutableSettings)
}

fun paymentSheetConfiguration(
playgroundState: PlaygroundState
): PaymentSheet.Configuration {
val builder = PaymentSheet.Configuration.Builder("Example, Inc.")
val configurationData =
val paymentSheetConfigurationData =
PlaygroundSettingDefinition.PaymentSheetConfigurationData(builder)
settings.onEach { (settingDefinition, value) ->
settingDefinition.configure(value, builder, playgroundState, configurationData)
settings.filter { (definition, _) ->
definition.applicable(configurationData)
}.onEach { (settingDefinition, value) ->
settingDefinition.configure(value, builder, playgroundState, paymentSheetConfigurationData)
}
return builder.build()
}
Expand All @@ -84,7 +107,9 @@ internal class PlaygroundSettings private constructor(

fun checkoutRequest(): CheckoutRequest {
val builder = CheckoutRequest.Builder()
settings.onEach { (settingDefinition, value) ->
settings.filter { (definition, _) ->
definition.applicable(configurationData)
}.onEach { (settingDefinition, value) ->
settingDefinition.configure(builder, value)
}
return builder.build()
Expand All @@ -102,12 +127,17 @@ internal class PlaygroundSettings private constructor(
val settingsMap = settings.filterKeys(filter).map {
val saveable = it.key.saveable()
if (saveable != null) {
saveable.key to JsonPrimitive(saveable.convertToString(it.value))
saveable.key to saveable.convertToString(it.value)
} else {
null
}
}.filterNotNull().toMap()
return Json.encodeToString(JsonObject(settingsMap))
return Json.encodeToString(
SerializableSettings(
configurationData = configurationData,
settings = settingsMap,
)
)
}

fun saveToSharedPreferences(context: Context) {
Expand Down Expand Up @@ -136,38 +166,48 @@ internal class PlaygroundSettings private constructor(
}
}

@Serializable
private class SerializableSettings(
val configurationData: PlaygroundConfigurationData,
val settings: Map<String, String>,
)

companion object {
private const val sharedPreferencesName = "PlaygroundSettings"
private const val sharedPreferencesKey = "json"

fun createFromDefaults(): PlaygroundSettings {
val defaultConfigurationData = PlaygroundConfigurationData()
val settings = allSettingDefinitions.associateWith { settingDefinition ->
MutableStateFlow(settingDefinition.defaultValue)
}.toMutableMap()
return PlaygroundSettings(settings)
return PlaygroundSettings(defaultConfigurationData, settings)
}

fun createFromJsonString(jsonString: String): PlaygroundSettings {
val settings: MutableMap<PlaygroundSettingDefinition<*>, MutableStateFlow<Any?>> =
mutableMapOf()
val jsonObject = Json.decodeFromString(JsonObject.serializer(), jsonString)
val settings: MutableMap<PlaygroundSettingDefinition<*>, MutableStateFlow<Any?>> = mutableMapOf()

val unserializedSettings = try {
samer-stripe marked this conversation as resolved.
Show resolved Hide resolved
Json.decodeFromString(SerializableSettings.serializer(), jsonString)
} catch (exception: SerializationException) {
Log.e("PlaygroundParsingError", "Error parsing settings from string", exception)

return createFromDefaults()
}

for (settingDefinition in allSettingDefinitions) {
val saveable = settingDefinition.saveable()
if (saveable != null) {
val jsonPrimitive = jsonObject[saveable.key] as? JsonPrimitive?
if (jsonPrimitive?.isString == true) {
settings[settingDefinition] =
MutableStateFlow(saveable.convertToValue(jsonPrimitive.content))
} else {
settings[settingDefinition] = MutableStateFlow(settingDefinition.defaultValue)
}
} else {
settingDefinition.saveable()?.let { saveable ->
val value = unserializedSettings.settings[saveable.key]?.let { stringValue ->
saveable.convertToValue(stringValue)
} ?: saveable.defaultValue

settings[settingDefinition] = MutableStateFlow(value)
} ?: run {
settings[settingDefinition] = MutableStateFlow(settingDefinition.defaultValue)
}
}

return PlaygroundSettings(settings)
return PlaygroundSettings(unserializedSettings.configurationData, settings)
}

fun createFromSharedPreferences(context: Context): PlaygroundSettings {
Expand All @@ -182,7 +222,7 @@ internal class PlaygroundSettings private constructor(
return createFromJsonString(jsonString)
}

val uiSettingDefinitions: List<PlaygroundSettingDefinition.Displayable<*>> = listOf(
private val uiSettingDefinitions: List<PlaygroundSettingDefinition.Displayable<*>> = listOf(
InitializationTypeSettingsDefinition,
CustomerSessionSettingsDefinition,
CustomerSettingsDefinition,
Expand All @@ -208,7 +248,6 @@ internal class PlaygroundSettings private constructor(
PaymentMethodOrderSettingsDefinition,
ExternalPaymentMethodSettingsDefinition,
LayoutSettingsDefinition,
IntegrationTypeSettingsDefinition,
)

private val nonUiSettingDefinitions: List<PlaygroundSettingDefinition<*>> = listOf(
Expand Down
Loading
Loading