Skip to content

ScreenViewHolder replaces View.* extensions and DecorativeViewFactory #712

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

Merged
merged 7 commits into from
Mar 31, 2022

Conversation

rjrjr
Copy link
Contributor

@rjrjr rjrjr commented Mar 29, 2022

Replaces #703.

The legacy workflow-ui world was built around a set of extension methods on View, backed by a WorkflowViewState object hanging off of a tag. This was overly clever, needlessly threw away a good bit of compile time safety, and made it much harder than it should be to implement wrapper renderings.

In particular, every ViewFactory was required to call View.bindShowRendering to put all this machinery in place. When a screen rendering wrapped another, with the intention of delegating to its registered ViewFactory, that required creating a wrapper ViewFactory that made a new bindShowRendering call replacing and wrapping the WorkflowViewState put in place by the delegate. That's so tricky it required the introduction of DecorativeViewFactory, which was still pretty tricky to use.

To solve all this, we reverse the relationship between View and WorkflowViewState, with the latter being replaced by ScreenViewHolder. Instead of returing a raw View, ScreenViewFactory.buildView now returns the view in a ScreenViewHolder. The call to ScreenViewHolder() replaces the old call to bindShowRendering, and the compiler makes it impossible to forget.

ScreenViewHolder is a box that holds:

  • a View
  • the (Screen, ViewEnvironment) -> Unit function that updates it (still fun interface ScreenViewRunner for ease of implementation)
  • and the latest ViewEnvironment that function received

It is worth noting that ScreenViewHolder.showing .canShow and .show() are all static extensions, built around the new Showing: ViewEnvironmentKey<Screen>. ScreenViewHolder itself is "write only", with an in ScreenT: Screen type parameter -- you can push strongly typed Screen renderings into it, but you can't pull them out again.

This minimalist ScreenViewHolder interface is trivial to wrap, so all the complexity that inspired DecorativeViewFactory is gone. It is replaced by the new ScreenViewFactory.unwrapping extension functions, whose use is demontrated in NamedScreenViewFactory, EnvironmentScreenViewFactory and BackButtonScreen.

The ViewStarter mechanism is also much simpler now, also no longer relying on any state stored in View tags. The implementation of ScreenViewFactory.start() remains a bit complex, but only because of the work it has to do to keep the legacy View.start() method working, in partnership with AsScreenViewFactory(). Once we delete the deprecated View code, most of the code in both of those can be deleted.

Fixes #413, fixes #598, fixes #626, fixes #551, fixes #443.

WorkflowViewStub show()

# https://sequencediagram.org/
title WorkflowViewStub.show()

participant "WorkflowViewStub" as stub
participant Screen
participant "ViewEnvironment" as env
participant "ScreenViewFactoryFinder" as finder
participant "ViewRegistry" as registry
participant "ScreenViewFactory" as factory
participant "ScreenViewHolder" as holder

[->stub:show(screen, env)

stub->Screen:buildView(env)
activate Screen
Screen->Screen:toViewFactory(env)
activate Screen
Screen->env:get(ScreenViewFactoryFinder)
Screen<--env:finder
Screen->finder:getViewFactoryForRendering(this)
activate finder
finder->registry:getEntryFor(screen)
finder<--registry:factory
Screen<--finder:factory
deactivate finder
deactivate Screen

Screen->factory:start(this, env)
activate factory
factory->factory:buildView(screen, env)
activate factory
factory->holder:init(screen, env, runner)
holder-->factory:holder
deactivate factory
factory->holder:show(screen, env)
activate holder
holder->holder:this.environment = env + (Showing to screen)
holder->holder:this.runner.show(screen, environment)
deactivate holder
Screen<--factory:holder
deactivate factory

stub<--Screen:holder
deactivate Screen


[->stub:show(screen, env)
activate stub
stub->holder:canShow(screen)
activate holder
holder->holder: compatible(this.environment[Showing], screen)
stub<--holder:true
deactivate holder
stub->holder:show(screen, env)
activate holder
holder->holder:this.environment = env + (Showing to screen)
holder->holder:this.runner.show(screen, environment)
deactivate holder
deactivate stub

@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch 3 times, most recently from 7a92315 to af8a144 Compare March 29, 2022 23:39
@rjrjr rjrjr marked this pull request as ready for review March 29, 2022 23:39
@rjrjr rjrjr requested review from a team and zach-klippenstein as code owners March 29, 2022 23:39
@rjrjr
Copy link
Contributor Author

rjrjr commented Mar 29, 2022

I've opened this open for review again because it's FINALLY GREEN. It's a bit sprawling, though, because a bunch of method renames that were previously in a separate PR have been squashed into this one. Looks like I dropped some important kdoc too.

I'm used up for today, I'll see if I can make it easier to digest in the morning. If anyone wants to dig in in the meantime, the meaty bits are ScreenViewFactory.kt and ScreenViewHolder.kt.

Replaces #703.

The legacy workflow-ui world was built around a set of extension methods on `View`, backed by a `WorkflowViewState` object hanging off of a tag. This was overly clever, needlessly threw away a good bit of compile time safety, and made it much harder than it should be to implement wrapper renderings.

In particular, every `ViewFactory` was required to call `View.bindShowRendering` to put all this machinery in place. When a screen rendering wrapped another, with the intention of delegating to its registered `ViewFactory`, that required creating a wrapper `ViewFactory` that made a new `bindShowRendering` call replacing and wrapping the `WorkflowViewState` put in place by the delegate. That's so tricky it required the introduction of `DecorativeViewFactory`, which was still pretty tricky to use.

To solve all this, we reverse the relationship between `View` and `WorkflowViewState`, with the latter being replaced by `ScreenViewHolder`. Instead of returing a raw `View`, `ScreenViewFactory.buildView` now returns the view in a `ScreenViewHolder`. The call to `ScreenViewHolder()` replaces the old call to `bindShowRendering`, and the compiler makes it impossible to forget.

`ScreenViewHolder` is a box that holds:

- a `View`
- the `(Screen, ViewEnvironment) -> Unit` function that updates it (still `fun interface ScreenViewRunner` for ease of implementation)
- and the latest `ViewEnvironment` that function received

It is worth noting that `ScreenViewHolder.showing` `.canShow` and `.show()` are all static extensions, built around the new `Showing: ViewEnvironmentKey<Screen>`. `ScreenViewHolder` itself is "write only", with an `in ScreenT: Screen` type parameter -- you can push strongly typed `Screen` renderings into it, but you can't pull them out again.

This minimalist `ScreenViewHolder` interface is trivial to wrap, so all the complexity that inspired `DecorativeViewFactory` is gone. It is replaced by the new `ScreenViewFactory.unwrapping` extension functions, whose use is demontrated in `NamedScreenViewFactory`, `EnvironmentScreenViewFactory` and `BackButtonScreen`.

The `ViewStarter` mechanism is also much simpler now, also no longer relying on any state stored in `View` tags. The implementation of `ScreenViewFactory.start()` remains a bit complex, but only because of the work it has to do to keep the legacy `View.start()` method working, in partnership with `AsScreenViewFactory()`. Once we delete the deprecated View code, most of the code in both of those can be deleted.

Fixes #413, fixes #598, fixes #626, fixes #551, fixes #443.

```
title WorkflowViewStub.show()

participant "WorkflowViewStub" as stub
participant Screen
participant "ViewEnvironment" as env
participant "ScreenViewFactoryFinder" as finder
participant "ViewRegistry" as registry
participant "ScreenViewFactory" as factory
participant "ScreenViewHolder" as holder

[->stub:show(screen, env)

stub->Screen:buildView(env)
activate Screen
Screen->Screen:toViewFactory(env)
activate Screen
Screen->env:get(ScreenViewFactoryFinder)
Screen<--env:finder
Screen->finder:getViewFactoryForRendering(this)
activate finder
finder->registry:getEntryFor(screen)
finder<--registry:factory
Screen<--finder:factory
deactivate finder
deactivate Screen

Screen->factory:start(this, env)
activate factory
factory->factory:buildView(screen, env)
activate factory
factory->holder:init(screen, env, runner)
holder-->factory:holder
deactivate factory
factory->holder:show(screen, env)
activate holder
holder->holder:this.environment = env + (Showing to screen)
holder->holder:this.runner.show(screen, environment)
deactivate holder
Screen<--factory:holder
deactivate factory

stub<--Screen:holder
deactivate Screen

[->stub:show(screen, env)
activate stub
stub->holder:canShow(screen)
activate holder
holder->holder: compatible(this.environment[Showing], screen)
stub<--holder:true
deactivate holder
stub->holder:show(screen, env)
activate holder
holder->holder:this.environment = env + (Showing to screen)
holder->holder:this.runner.show(screen, environment)
deactivate holder
deactivate stub
```
@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch from af8a144 to 05adc0e Compare March 30, 2022 17:04
}
ScreenViewHolder(initialEnvironment, view) { rendering, environment ->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Notice how bindShowRendering calls migrate to ScreenViewHolder instantiation.


originalFactory.start(rendering, viewEnvironment, context, container = null)
.let { viewHolder ->
// Put the viewHolder in a tag so that we can find it in the update lambda, below.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is our only remaining use of View.setTag. I don't see an alternative.

}
},
ScreenViewHolder(environment, frame) { rendering, viewEnvironment ->
stub.show(asScreen(TestModals(listOf(rendering.wrapped))), viewEnvironment)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Compare this with #703, where we were forced to cast (stub as WorkflowViewStub)


// Check that the state was NOT restored.
assertThat(firstViewRestored.viewState).isEqualTo("")
}

@Test fun throws_when_view_not_bound() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This mistake is now impossible.

internal
fun <WrappedT : Screen> NamedScreenViewFactory(): ScreenViewFactory<NamedScreen<WrappedT>> {
return forBuiltView { initialNamedScreen, initialEnvironment, context, container ->
initialNamedScreen.wrapped.toViewFactory(initialEnvironment)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Less concise, but I hope also less mysterious.

)
internal fun <WrappedT : Screen> EnvironmentScreenViewFactory():
ScreenViewFactory<EnvironmentScreen<WrappedT>> {
return forBuiltView { initialEnvScreen, initialEnvironment, context, container ->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not crazy about this name. forBuilder? forConstructor? fromCode?

Copy link
Contributor

Choose a reason for hiding this comment

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

Whether it is reading this for hours or not I am not sure, but it is clicking with me.

Copy link
Contributor

@RBusarow RBusarow Mar 31, 2022

Choose a reason for hiding this comment

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

I didn't love forBuiltView in the previous PR, but eventually got comfortable with it. This week, I had to re-learn what it meant. Without the context of having just looked at its siblings, I don't think it makes a lot of sense.

Perhaps forProgrammaticView? Or forViewFromCode so that it's consistent with the for prefix. Or use from___ for them all.

Copy link
Contributor

Choose a reason for hiding this comment

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

I liked forXYZ so much more than bind but now I'm jumping back on here to say I do love @RBusarow 's from__ suggestion! or even buildFrom__

buildFromCode buildFromLayoutResource buildFromBinding

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like from_ a lot. Re: build, I like building even better. But either one is verbose, and I've had to waste so much time coping with line-too-long lints.


assertThat(wrappedScreen.viewFactory.lastEnv!![SomeEnvValue]).isEqualTo("bye")

// TODO To be fixed or obviated by https://github.com/square/workflow-kotlin/pull/703
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODONE

@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch from 05adc0e to 701bc09 Compare March 30, 2022 18:23
@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch from d2d7b5d to 3985107 Compare March 30, 2022 20:24
@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch from 3985107 to 70d95d4 Compare March 30, 2022 21:46
rjrjr added 2 commits March 30, 2022 14:48
Renames the various `ScreenViewRunner.bind` methods to `ScreenViewFactory.for*`,
and adds the missing kdoc for `ScreenViewRunner`, `ScreenViewFactory` and `ScreenViewHolder`.
@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch from 70d95d4 to f046a3b Compare March 30, 2022 21:49
Copy link
Contributor

@steve-the-edwards steve-the-edwards left a comment

Choose a reason for hiding this comment

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

Halfway through. will break until tomorrow. Looks good so far! Really like the shape of the holder more!

@@ -89,7 +89,7 @@ class OverviewDetailContainer(view: View) : ScreenViewRunner<OverviewDetailScree
stub.show(combined, viewEnvironment + Single)
}

companion object : ScreenViewFactory<OverviewDetailScreen> by ScreenViewRunner.bind(
companion object : ScreenViewFactory<OverviewDetailScreen> by ScreenViewFactory.forLayoutResource(
Copy link
Contributor

Choose a reason for hiding this comment

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

Very grateful for these forXYZ renamings!

@@ -83,11 +82,13 @@ class BoardView(context: Context) : View(context) {
}

@OptIn(WorkflowUiExperimentalApi::class)
companion object : ScreenViewFactory<Board> by ManualScreenViewFactory(
type = Board::class,
Copy link
Contributor

Choose a reason for hiding this comment

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

You probably explained this and I read it but didn't fully understand but why don't we need the Screen/Rendering type passed in anymore? Isn't this still the key for ViewRegistry?

type = ClickyTextRendering::class,
viewConstructor = { initialRendering, initialEnv, context, _ ->
TextView(context).also { textView ->
override val viewFactory = ScreenViewFactory.forBuiltView<ClickyTextRendering>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh i see we type-parametrized the method.

Copy link
Contributor

@steve-the-edwards steve-the-edwards left a comment

Choose a reason for hiding this comment

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

😓 . Ok. I think this will be great and much easier to follow. Lots of comments on naming and such though. If you think these can all just live as backlog items for while you are away I guess i'm OK with that?

@@ -34,14 +33,14 @@ internal class BackStackContainerLifecycleActivity : AbstractLifecycleTestActivi
* Default rendering always shown in the backstack to simplify test configuration.
*/
object BaseRendering : Screen, ScreenViewFactory<BaseRendering> {
override val type: KClass<in BaseRendering> = BaseRendering::class
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this just unnecessary or is there something significant to losing the explicit declared type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unnecessary, the factory function is already doing that for us.

Comment on lines 29 to 30
by ScreenViewFactory.forBuiltView(
buildView = { _, initialEnvironment, context, _ ->
Copy link
Contributor

Choose a reason for hiding this comment

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

You use a mix of ScreenViewFactory.forBuiltView and just forBuiltView and a mix of named buildView vs anonymous.

Is this just to show options? Or do we want to standardize in the sample code?

Copy link
Contributor

Choose a reason for hiding this comment

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

Personally I would lean towards the ScreenViewFactory.forBuiltView and the named argument as I think it reads more clearly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Combination of rushing and avoiding long lines, and don't care very much about consistency in non-sample code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the sample code, agree with you that ScreenViewFactory.forBuiltView is much better.

Comment on lines 22 to 23
* Use [forLayoutResource], [forViewBinding], etc., to create a [ScreenViewFactory]
* from a [ScreenViewRunner].
Copy link
Contributor

Choose a reason for hiding this comment

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

This is confusing. At first it sounded like forLayoutResource and forViewBinding are extensions on ScreenViewRunner - which they are not. They accept a functional argument that constructs a ScreenViewRunner for the named component - a 'layout resource' or a 'view binding'.

what about something like?

Suggested change
* Use [forLayoutResource], [forViewBinding], etc., to create a [ScreenViewFactory]
* from a [ScreenViewRunner].
* Use [forLayoutResource], [forViewBinding], etc., to create a [ScreenViewFactory].
* These helper methods take the resource, view, and/or view binding as arguments
* along with a constructor function that creates the `ScreenViewRunner` `showRendering`
* function.

As I type that out I realize how important it is that we just think of the ScreenViewRunner as a way to refer to the type parameterized showRendering function for any ScreenT. That is the important bit and I want to keep drawing back references to that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Much better, thanks.

Copy link
Contributor Author

@rjrjr rjrjr Mar 31, 2022

Choose a reason for hiding this comment

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

 * Use [forLayoutResource], [forViewBinding], etc., to create a [ScreenViewFactory].
 * These helper methods take a layout resource, view binding, or view building
 * function as arguments, along with a factory to create a [showRendering]
 * [ScreenViewRunner.showRendering] function.

Choose a reason for hiding this comment

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

I was about to ask the purpose of ScreenViewRunner if it was a way to keep Holder as static/fixed as possible, and place the polymorphism in the Runner. Like a "strategy". From the comments I think that's the reason. :)

* will hold a reference to [screen] with key [Showing].
*/
@WorkflowUiExperimentalApi
public fun <ScreenT : Screen> ScreenViewHolder<ScreenT>.show(
Copy link
Contributor

Choose a reason for hiding this comment

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

remind me again why these are extensions rather than implemented in the interface? We just want them to be final to avoid footguns?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

More than that: when wrapping, it's to prevent recursive calls from clobbering environment[Showing] with the nested rendering type. I'll add an inline comment to call that out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  // Why is this an extension rather than part of the interface?
  // When wrapping, we need to prevent recursive calls from clobbering
  // `environment[Showing]` with the nested rendering type.
  runner.showRendering(screen, environment + (Showing to screen))

@@ -38,7 +38,7 @@ public fun <RenderingT : Any> ViewRegistry.getFactoryFor(
return getEntryFor(renderingType) as? ViewFactory<RenderingT>
}

@Deprecated("Use Screen.buildView")
@Deprecated("Use Screen.toView")
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this actually what the extension is still called? I don't remember toView

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It became buildView, but now I've gone ahead and inlined it! Will fix.

)
internal fun <WrappedT : Screen> EnvironmentScreenViewFactory():
ScreenViewFactory<EnvironmentScreen<WrappedT>> {
return forBuiltView { initialEnvScreen, initialEnvironment, context, container ->
Copy link
Contributor

Choose a reason for hiding this comment

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

Whether it is reading this for hours or not I am not sure, but it is clicking with me.


@WorkflowUiExperimentalApi
public inline fun <reified T : Any> ViewEnvironmentKey(
crossinline produceDefault: () -> T,
Copy link
Contributor

Choose a reason for hiding this comment

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

Wha? Was this just for the NothingShowing case? Why did that have to be a function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It didn't have to be a function, it's just a convenience that makes it easier to implement ViewEnvironmentKey, should have written it two years ago. I'd like to make a pass and use it universally after this merges.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#715.

) : ScreenViewHolder<ScreenT> {

private var _environment: ViewEnvironment = initialEnvironment
override val environment: ViewEnvironment get() = _environment

Choose a reason for hiding this comment

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

nit: I think you could use private set:

override var environment: ViewEnvironment = initialEnvironment
  private set

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't, the public API is val. But if I drop the interface, then hell yeah.

*
* Also see the simpler variant of this function that takes only an [unwrap] argument.
*
* ## Example

Choose a reason for hiding this comment

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

It sounded to me like the two examples were different in shape, but they look the same. Same structure, same calls? (not saying it's bad, just asking if I'm missing any difference)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You know, you're right. I originally had dropped the second one, but then tired me thought it actually covered more ground.

It really doesn't, deleting it.

* allow [WorkflowViewStub.show] to call it for you.
*/
@WorkflowUiExperimentalApi
public interface ScreenViewHolder<in ScreenT : Screen> {

Choose a reason for hiding this comment

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

Is the plan to make this type as sealed as possible? I'm asking because the polymorphism is extracted out to the runner.
If the answer is yes this could be a class, or a sealed class at least, or something. Definitely not important.

But maybe worth mentioning in the docs:

  • holder: pretty static / fixed / controlled / not-instantiable from outside, etc
  • runner: the "strategy" (strategy pattern) to implement a particular holder.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I kept bouncing back and forth btw. using an interface or just making RealScreenViewHolder public (and dropping the Real prefix, obv.). I forget what finally drove me back to the interface, something related to ease of wrapping. Unclear to me that it's still necessary, I'll take one more look at dropping

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I can pull this off, but I'd prefer to merge what I have and tackle it in a follow up. In the meantime I'll try to punch up the docs as you suggest.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The important thing about the interface approach is that it makes it easy to wrap a ScreenViewHolder and delegate to the environment of the wrapped instance, which is crucial for our crazy showing semantics. I suppose we could achieve that another way, but I don't really see what the benefit would be.

This is the case that drove me to write EnvironmentScreenAndroidIntegrationTest last week.

Copy link

@helios175 helios175 left a comment

Choose a reason for hiding this comment

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

I've been wandering around the code for 1h+ and everything makes sense so far.
This is awesome!

Some comments around making sure we understand the purpose (as in Matrix's Agent Smith purpose) of each class, and making sure !purpose(Holder).intersects(purpose(Runner))

@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch 2 times, most recently from 97c72dd to 9096cc6 Compare March 31, 2022 22:19
rjrjr added 2 commits March 31, 2022 15:40
- `ScreenViewFactory.unwrapping` > `.toUnwrappingViewFactory`, and deleted redundant kdoc
- `ScreenViewFactory.start` > `.startShowing`
- Inlined `Screen.buildView`
Emphasizing that WorkflowViewStub is the right thing, and that ScreenViewRunner is the strategy object that feature developers implement.
@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch from 9096cc6 to c4211f8 Compare March 31, 2022 22:51
`ScreenViewFactory.fromCode()`, lovely.
@rjrjr rjrjr force-pushed the ray/fix-goodbye-view-extensions-i-hope branch from c4211f8 to 3f9ee9c Compare March 31, 2022 23:00
@rjrjr rjrjr merged commit ec37f4d into ray/ui-update Mar 31, 2022
@rjrjr rjrjr deleted the ray/fix-goodbye-view-extensions-i-hope branch March 31, 2022 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants