Skip to content
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
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ android.useAndroidX=true
systemProp.org.gradle.internal.publish.checksums.insecure=true

GROUP=com.squareup.workflow1
VERSION_NAME=1.8.0-uiUpdate03-SNAPSHOT
VERSION_NAME=1.8.0-uiUpdate04-SNAPSHOT

POM_DESCRIPTION=Square Workflow

Expand Down
7 changes: 6 additions & 1 deletion workflow-ui/core-android/api/core-android.api
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,12 @@ public abstract interface class com/squareup/workflow1/ui/ScreenViewHolder {
}

public final class com/squareup/workflow1/ui/ScreenViewHolder$Companion {
public final fun getShowing ()Lcom/squareup/workflow1/ui/ViewEnvironmentKey;
}

public final class com/squareup/workflow1/ui/ScreenViewHolder$Companion$Showing : com/squareup/workflow1/ui/ViewEnvironmentKey {
public static final field INSTANCE Lcom/squareup/workflow1/ui/ScreenViewHolder$Companion$Showing;
public fun getDefault ()Lcom/squareup/workflow1/ui/Screen;
public synthetic fun getDefault ()Ljava/lang/Object;
}

public final class com/squareup/workflow1/ui/ScreenViewHolder$Companion$ShowingNothing : com/squareup/workflow1/ui/Screen {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ public interface ScreenViewHolder<in ScreenT : Screen> {
* Provides access to the [Screen] instance most recently shown in a [ScreenViewHolder]'s
* [view] via [show]. Call [showing] for more convenient access.
*/
public val Showing: ViewEnvironmentKey<Screen> = ViewEnvironmentKey { ShowingNothing }
public object Showing : ViewEnvironmentKey<Screen>(Screen::class) {
override val default: Screen = ShowingNothing
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.squareup.workflow1.ui.Compatible.Companion.keyFor
import com.squareup.workflow1.ui.NamedScreen
import com.squareup.workflow1.ui.R
import com.squareup.workflow1.ui.ScreenViewHolder
import com.squareup.workflow1.ui.ScreenViewHolder.Companion.Showing
import com.squareup.workflow1.ui.ViewEnvironment
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.androidx.WorkflowAndroidXSupport.stateRegistryOwnerFromViewTreeOrContext
Expand Down Expand Up @@ -65,7 +66,7 @@ public open class BackStackContainer @JvmOverloads constructor(
newRendering: BackStackScreen<*>,
newViewEnvironment: ViewEnvironment
) {
savedStateParentKey = keyFor(newViewEnvironment[ScreenViewHolder.Showing])
savedStateParentKey = keyFor(newViewEnvironment[Showing])

val config = if (newRendering.backStack.isEmpty()) First else Other
val environment = newViewEnvironment + config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.squareup.workflow1.ui.Compatible.Companion.keyFor
import com.squareup.workflow1.ui.R
import com.squareup.workflow1.ui.ScreenViewFactory
import com.squareup.workflow1.ui.ScreenViewHolder
import com.squareup.workflow1.ui.ScreenViewHolder.Companion.Showing
import com.squareup.workflow1.ui.ViewEnvironment
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.WorkflowViewStub
Expand Down Expand Up @@ -82,7 +83,7 @@ internal class BodyAndModalsContainer @JvmOverloads constructor(
newScreen: BodyAndModalsScreen<*, *>,
viewEnvironment: ViewEnvironment
) {
savedStateParentKey = keyFor(viewEnvironment[ScreenViewHolder.Showing])
savedStateParentKey = keyFor(viewEnvironment[Showing])

val showingModals = newScreen.modals.isNotEmpty()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ package com.squareup.workflow1.ui.container
import com.squareup.workflow1.ui.DecorativeViewFactory
import com.squareup.workflow1.ui.ViewFactory
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.merge

@Suppress("DEPRECATION")
@WorkflowUiExperimentalApi
internal object EnvironmentScreenLegacyViewFactory : ViewFactory<EnvironmentScreen<*>>
by DecorativeViewFactory(
type = EnvironmentScreen::class,
map = { environmentScreen, inheritedEnvironment ->
Pair(environmentScreen.wrapped, environmentScreen.environment merge inheritedEnvironment)
Pair(environmentScreen.wrapped, environmentScreen.environment + inheritedEnvironment)
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.ScreenViewFactory
import com.squareup.workflow1.ui.ScreenViewFactory.Companion.fromCode
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.merge
import com.squareup.workflow1.ui.toUnwrappingViewFactory
import com.squareup.workflow1.ui.toViewFactory

@WorkflowUiExperimentalApi
internal fun <WrappedT : Screen> EnvironmentScreenViewFactory():
ScreenViewFactory<EnvironmentScreen<WrappedT>> {
return fromCode { initialEnvScreen, initialEnvironment, context, container ->
val mergedInitialEnvironment = initialEnvironment merge initialEnvScreen.environment
val mergedInitialEnvironment = initialEnvironment + initialEnvScreen.environment

initialEnvScreen.wrapped.toViewFactory(mergedInitialEnvironment)
.toUnwrappingViewFactory<EnvironmentScreen<WrappedT>, WrappedT>(
unwrap = { it.wrapped },
showWrapperScreen = { _, envScreen, environment, showUnwrapped ->
showUnwrapped(envScreen.wrapped, environment merge envScreen.environment)
showUnwrapped(envScreen.wrapped, environment + envScreen.environment)
}
)
.buildView(initialEnvScreen, mergedInitialEnvironment, context, container)
Expand Down
11 changes: 4 additions & 7 deletions workflow-ui/core-common/api/core-common.api
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,11 @@ public final class com/squareup/workflow1/ui/ViewEnvironment$Companion {

public abstract class com/squareup/workflow1/ui/ViewEnvironmentKey {
public fun <init> (Lkotlin/reflect/KClass;)V
public fun combine (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
public final fun equals (Ljava/lang/Object;)Z
public abstract fun getDefault ()Ljava/lang/Object;
public final fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/squareup/workflow1/ui/ViewEnvironmentKt {
public static final synthetic fun ViewEnvironmentKey (Lkotlin/jvm/functions/Function0;)Lcom/squareup/workflow1/ui/ViewEnvironmentKey;
public final fun toString ()Ljava/lang/String;
}

public abstract interface class com/squareup/workflow1/ui/ViewRegistry {
Expand All @@ -119,6 +116,8 @@ public abstract interface class com/squareup/workflow1/ui/ViewRegistry {
}

public final class com/squareup/workflow1/ui/ViewRegistry$Companion : com/squareup/workflow1/ui/ViewEnvironmentKey {
public fun combine (Lcom/squareup/workflow1/ui/ViewRegistry;Lcom/squareup/workflow1/ui/ViewRegistry;)Lcom/squareup/workflow1/ui/ViewRegistry;
public synthetic fun combine (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
public fun getDefault ()Lcom/squareup/workflow1/ui/ViewRegistry;
public synthetic fun getDefault ()Ljava/lang/Object;
}
Expand All @@ -131,8 +130,6 @@ public final class com/squareup/workflow1/ui/ViewRegistryKt {
public static final fun ViewRegistry ()Lcom/squareup/workflow1/ui/ViewRegistry;
public static final fun ViewRegistry ([Lcom/squareup/workflow1/ui/ViewRegistry$Entry;)Lcom/squareup/workflow1/ui/ViewRegistry;
public static final synthetic fun get (Lcom/squareup/workflow1/ui/ViewRegistry;Lkotlin/reflect/KClass;)Lcom/squareup/workflow1/ui/ViewRegistry$Entry;
public static final fun merge (Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/ViewEnvironment;)Lcom/squareup/workflow1/ui/ViewEnvironment;
public static final fun merge (Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/ViewRegistry;)Lcom/squareup/workflow1/ui/ViewEnvironment;
public static final fun merge (Lcom/squareup/workflow1/ui/ViewRegistry;Lcom/squareup/workflow1/ui/ViewRegistry;)Lcom/squareup/workflow1/ui/ViewRegistry;
public static final fun plus (Lcom/squareup/workflow1/ui/ViewEnvironment;Lcom/squareup/workflow1/ui/ViewRegistry;)Lcom/squareup/workflow1/ui/ViewEnvironment;
public static final fun plus (Lcom/squareup/workflow1/ui/ViewRegistry;Lcom/squareup/workflow1/ui/ViewRegistry$Entry;)Lcom/squareup/workflow1/ui/ViewRegistry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,30 @@ public class ViewEnvironment
constructor(
public val map: Map<ViewEnvironmentKey<*>, Any> = emptyMap()
) {
@Suppress("UNCHECKED_CAST")
public operator fun <T : Any> get(key: ViewEnvironmentKey<T>): T = map[key] as? T ?: key.default
public operator fun <T : Any> get(key: ViewEnvironmentKey<T>): T = getOrNull(key) ?: key.default

@Suppress("DEPRECATION")
public operator fun <T : Any> plus(pair: Pair<ViewEnvironmentKey<T>, T>): ViewEnvironment =
ViewEnvironment(map + pair)
public operator fun <T : Any> plus(pair: Pair<ViewEnvironmentKey<T>, T>): ViewEnvironment {
val (newKey, newValue) = pair
val newPair = getOrNull(newKey)
?.let { oldValue -> newKey to newKey.combine(oldValue, newValue) }
?: pair
@Suppress("DEPRECATION")
return ViewEnvironment(map + newPair)
}

@Suppress("DEPRECATION")
public operator fun plus(other: ViewEnvironment): ViewEnvironment {
if (this == other) return this
if (other.map.isEmpty()) return this
if (this.map.isEmpty()) return other
return ViewEnvironment(map + other.map)
if (map.isEmpty()) return other
val newMap = map.toMutableMap()
other.map.entries.forEach { (key, value) ->
@Suppress("UNCHECKED_CAST")
newMap[key] = getOrNull(key as ViewEnvironmentKey<Any>)
?.let { oldValue -> key.combine(oldValue, value) }
?: value
}
return ViewEnvironment(newMap)
}

override fun toString(): String = "ViewEnvironment($map)"
Expand All @@ -41,6 +52,9 @@ constructor(

override fun hashCode(): Int = map.hashCode()

@Suppress("UNCHECKED_CAST")
private fun <T : Any> getOrNull(key: ViewEnvironmentKey<T>): T? = map[key] as? T
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth to make it getOrDefault, as all callers are using some sort of fallback.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the main call wants to run a lambda only in the non-null case.


public companion object {
@Suppress("DEPRECATION")
public val EMPTY: ViewEnvironment = ViewEnvironment()
Expand All @@ -57,6 +71,15 @@ public abstract class ViewEnvironmentKey<T : Any>(
) {
public abstract val default: T

/**
* Applied from [ViewEnvironment.plus] when the receiving environment already contains
* a value for this key. The default implementation replaces [left] with [right].
*/
public open fun combine(
left: T,
right: T
): T = right

final override fun equals(other: Any?): Boolean = when {
this === other -> true
other != null && this::class != other::class -> false
Expand All @@ -65,17 +88,7 @@ public abstract class ViewEnvironmentKey<T : Any>(

final override fun hashCode(): Int = type.hashCode()

override fun toString(): String {
final override fun toString(): String {
return "${this::class.simpleName}(${type.simpleName})"
}
}

@WorkflowUiExperimentalApi
public inline fun <reified T : Any> ViewEnvironmentKey(
crossinline produceDefault: () -> T,
): ViewEnvironmentKey<T> {
return object : ViewEnvironmentKey<T>(T::class) {
override val default: T
get() = produceDefault()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ public interface ViewRegistry {

public companion object : ViewEnvironmentKey<ViewRegistry>(ViewRegistry::class) {
override val default: ViewRegistry get() = ViewRegistry()
override fun combine(
left: ViewRegistry,
right: ViewRegistry
): ViewRegistry = left.merge(right)
}
}

Expand All @@ -103,24 +107,24 @@ public fun ViewRegistry(vararg bindings: Entry<*>): ViewRegistry =
public fun ViewRegistry(): ViewRegistry = TypedViewRegistry()

/**
* @throws IllegalArgumentException if the receiver already has a matching [entry].
* Transforms the receiver to add [entry], throwing [IllegalArgumentException] if the receiver
* already has a matching [entry]. Use [merge] to replace an existing entry with a new one.
*/
@WorkflowUiExperimentalApi
public operator fun ViewRegistry.plus(entry: Entry<*>): ViewRegistry =
this + ViewRegistry(entry)

/** @throws IllegalArgumentException if other has redundant entries. */
/**
* Transforms the receiver to add all entries from [other], throwing [IllegalArgumentException]
* if the receiver already has any matching [entry]. Use [merge] to replace existing entries.
*/
@WorkflowUiExperimentalApi
public operator fun ViewRegistry.plus(other: ViewRegistry): ViewRegistry {
if (other.keys.isEmpty()) return this
if (this.keys.isEmpty()) return other
return CompositeViewRegistry(this, other)
}

/**
* Replaces the existing [ViewRegistry] of the receiver with [registry]. Use
* [ViewEnvironment.merge] to combine them instead.
*/
@WorkflowUiExperimentalApi
public operator fun ViewEnvironment.plus(registry: ViewRegistry): ViewEnvironment {
if (this[ViewRegistry] === registry) return this
Expand All @@ -144,32 +148,3 @@ public infix fun ViewRegistry.merge(other: ViewRegistry): ViewRegistry {
.toTypedArray()
.let { ViewRegistry(*it) }
}

/**
* Merges the [ViewRegistry] of the receiver with [registry]. If there are conflicting entries,
* those in [registry] are preferred.
*/
@WorkflowUiExperimentalApi
public infix fun ViewEnvironment.merge(registry: ViewRegistry): ViewEnvironment {
if (this[ViewRegistry] === registry) return this
if (registry.keys.isEmpty()) return this

val merged = this[ViewRegistry] merge registry
return this + merged
}

/**
* Combines the receiving [ViewEnvironment] with [other], taking care to merge
* their [ViewRegistry] entries. Any other conflicting values in [other] replace those
* in the receiver.
*/
@WorkflowUiExperimentalApi
public infix fun ViewEnvironment.merge(other: ViewEnvironment): ViewEnvironment {
if (this == other) return this
if (other.map.isEmpty()) return this
if (this.map.isEmpty()) return other

val oldReg = this[ViewRegistry]
val newReg = other[ViewRegistry]
return this + other + (ViewRegistry to oldReg.merge(newReg))
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.ViewEnvironment
import com.squareup.workflow1.ui.ViewRegistry
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.merge
import com.squareup.workflow1.ui.plus

/**
* Pairs a [wrapped] rendering with a [environment] to support its display.
Expand Down Expand Up @@ -36,7 +36,7 @@ public class EnvironmentScreen<V : Screen>(
*/
@WorkflowUiExperimentalApi
public fun Screen.withRegistry(viewRegistry: ViewRegistry): EnvironmentScreen<*> {
return withEnvironment(ViewEnvironment.EMPTY merge viewRegistry)
return withEnvironment(ViewEnvironment.EMPTY + viewRegistry)
}

/**
Expand All @@ -53,7 +53,7 @@ public fun Screen.withEnvironment(
return when (this) {
is EnvironmentScreen<*> -> {
if (environment.map.isEmpty()) this
else EnvironmentScreen(wrapped, this.environment merge environment)
else EnvironmentScreen(wrapped, this.environment + environment)
}
else -> EnvironmentScreen(this, environment)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,22 @@ internal class ViewEnvironmentTest {
val environment = EMPTY + (StringHint to "able")
assertThat(environment + environment).isSameInstanceAs(environment)
}

@Test fun `honors combine`() {
val combiningHint = object : ViewEnvironmentKey<String>(String::class) {
override val default: String
get() = error("")

override fun combine(
left: String,
right: String
): String {
return "$left-$right"
}
}

val left = EMPTY + (combiningHint to "able")
val right = EMPTY + (combiningHint to "baker")
assertThat((left + right)[combiningHint]).isEqualTo("able-baker")
}
}
Loading