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

Replace CodeListener with RedwoodView.Root #2374

Merged
merged 8 commits into from
Oct 16, 2024

Conversation

squarejesse
Copy link
Contributor

@squarejesse squarejesse commented Oct 8, 2024

This moves loading/error/ready tracking to live beside the content, rather than as something that side-effects the content.

This is not compatible with the old API and there is no shim.

Closes: #2174


  • CHANGELOG.md's "Unreleased" section has been updated, if applicable.

This moves loading/error/ready tracking to live beside the
content, rather than as something that side-effects the
content.

This is not compatible with the old API and there is no
shim.
// Canonicalize "java.lang.Exception(boom!)" to "kotlin.Exception(boom!)".
val exceptionString = uncaughtException?.toString()?.replace("java.lang.", "kotlin.")

// TODO(jwilson): this is a backwards-compatibility shim. Emit a simpler event.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For follow-up

@@ -37,17 +37,16 @@ import kotlinx.coroutines.flow.StateFlow
@SuppressLint("ViewConstructor")
public open class RedwoodLayout(
context: Context,
public final override val root: RedwoodView.Root<View> = ViewRoot(context),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

On Android, this accepts the interface type, RedwoodView.Root ...

@@ -38,17 +38,16 @@ import platform.UIKit.UIUserInterfaceStyle
import platform.UIKit.UIView

public open class RedwoodUIView(
public val view: UIView,
final override val root: UIViewRoot,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

But on iOS, this accepts our concrete base implementation. This is because I haven’t yet figured out how to add a bounds change listener to a view I didn’t subclass

@@ -6,19 +6,18 @@ class CounterViewController : UIViewController {
private var delegate: CounterViewControllerDelegate!

override func viewDidLoad() {
let container = UIStackView()
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 a behavior change. For a follow-up I need to use a Row in the Compose-side code for the counter.

}
}

for (child in viewGroupChildren) {
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 still isn’t that great . . . I prefer the way this works in Compose

import androidx.appcompat.view.ContextThemeWrapper

@SuppressLint("ViewConstructor")
internal class LoadingView(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

New: now there’s a spinner when launching emoji search

@squarejesse
Copy link
Contributor Author

@JakeWharton this is ready for review.

I did an in-place swap from the old type to the new type rather than a graceful migration, which makes the PR larger than I generally like.

@JakeWharton
Copy link
Collaborator

Yeah it's... Big. I started today but had to leave early. Will finish tomorrow morning.

Copy link
Collaborator

@JakeWharton JakeWharton left a comment

Choose a reason for hiding this comment

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

I reviewed as much as I could. Don't want to hold it up any more.


for (child in viewGroupChildren) {
if (child is ExceptionView || child is LoadingView) {
removeView(child)
Copy link
Collaborator

Choose a reason for hiding this comment

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

It is not safe to remove a view like this while iterating. However, the iterator is actually a MutableIterator that you can call remove() on: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:core/core-ktx/src/main/java/androidx/core/view/ViewGroup.kt;l=89-99;drc=1e02caf7787e6fc64ec1913bf6e5cbbab1724a13. Unfortunately that means you can't use a nice for loop anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh hell yes. I was exploiting the fact that this will only contain one child element if its either of those types. But I prefer your fix with an iterator.

Comment on lines +129 to 134
if (loadCount == 0) {
addView(LoadingView(context))
}
if (uncaughtException != null) {
addView(ExceptionView(context, uncaughtException))
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems a bit suspect that the loop will leave both LoadingView and ExceptionView instances, but then also possible add new ones as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. In practice it’s unlikely to transition from loading to loading again, or exception to exception again.

*/
public fun reset()
@ObjCName("Root", exact = true)
public interface Root<W : Any> : Widget<W> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems like it exists at the wrong layer of abstraction since Redwood has no concept of loading or errors–just content. Eliminating CodeListener is a big enough win for now. We can figure out how to move it later.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see why this extends Widget. Do we use that capability anywhere? I haven't seen any usage that relies on it, but perhaps that's just because the diff is pretty big.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We’re using the value field to get a component to place in the parent container. But that doesn’t need to be polymorphic.

squarejesse and others added 2 commits October 16, 2024 13:45
…/redwood/emojisearch/android/views/EmojiSearchActivity.kt

Co-authored-by: Jake Wharton <[email protected]>
@squarejesse squarejesse enabled auto-merge (squash) October 16, 2024 17:52
@squarejesse squarejesse merged commit d32766e into trunk Oct 16, 2024
11 checks passed
@squarejesse squarejesse deleted the jwilson.1004.redwoodview_root branch October 16, 2024 19:10
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.

Use a Redwood schema for loading and error UIs
2 participants