-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Conversation
|
@@ -341,7 +341,13 @@ class SessionControlAdapter( | |||
|
|||
override fun onViewRecycled(holder: RecyclerView.ViewHolder) { | |||
when (holder) { | |||
is PocketStoriesViewHolder -> holder.composeView.disposeComposition() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was seeing as much as 300ms needed for composeView.onAttachedToWindow
everytime the holder was scrolled off and on screen again.
I don't think we need the composable destroyed while the user is still on homescreen and keeping it around definitely means a smoother UX.
} | ||
} | ||
|
||
// Populate each row with as many items as possible combining wider with narrower items. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without this there would be a lot of unused space.
...in/java/org/mozilla/fenix/home/sessioncontrol/viewholders/pocket/PocketStoriesComposables.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First pass. Haven't read all of it yet. Especially the grid code not yet.
} | ||
} | ||
|
||
homeFragmentStore.dispatch(HomeFragmentAction.PocketStoriesCategoriesChange(updatedCategories)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The actual creation of the new state (the new categories) should preferably happen in the reducer. That way you are guaranteed to always end up with a consistent state. Imagine the processing being slow and the user clicking categories fast: You may generate states here while the new state is not available in the store yet, which will cause inconsistent states. This doesn't not happen in the reducer where the actions will be processed sequentially.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remembered that I tried that first but the reducer seemed a bit big and it seemed to now contain the logic on for how to handle categories being clicked so I then moved that code in composable's parent - this fragment.
#21104 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think reducers and state updates happen only on the main thread?
If so, instead of me launching coroutines to calculate the updated state and dispatch that I could have all this running on main.
That would avoid reducers becoming too complex while the new state can be calculated a bit closer to the user interaction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think actions like "SelectCategory" or "DeselectCategory" make totally sense to me - and I don't think it's a problem if the reducer is complex, if the calculation of the new state is complex (as long it's still a function that just transforms input into an output without side effects). If I remember correctly the first approach had something like a "Clicked" action and that felt wrong because that is too close to the UI layer, the fragment handles the click and would dispatch an actual state changing action (not an UI event like "click").
* This only impacts filtered results guaranteeing an even spread of stories from each category. | ||
*/ | ||
@VisibleForTesting | ||
internal fun getFilteredStories( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like those helpers here are independent from the HomeFragment
class. Maybe we can move them somewhere else. There's a high risk that the HomeFragment
will grow a lot. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe into a Controller
😅?
Was thinking about an intermediate big interface for all domain logic needed for home composables, something like HomeComposablesLogic
?(please suggest a better name) with a default implementation that can be delegated to by the HomeFragment?
This just while migrating current views to composables, though even after fully migrating this screen to Compose, if bringing back all this logic in one place will have that again being immense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a strong opinion on the naming. It just seems like some utility functions (or maybe they could even be extension functions?) that could move to some related utilities file. Just to keep the fragment focused.
Sorry for not keeping this up-to-date with main. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving, but think the actual state creation should move into the reducer - that's it's job. :)
This pull request has conflicts when rebasing. Could you fix it @Mugurell? 🙏 |
|
Restarting CI. |
This pull request has conflicts when rebasing. Could you fix it @Mugurell? 🙏 |
context.state.getFilteredStories(POCKET_STORIES_TO_SHOW_COUNT) | ||
) | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand the need for this middleware. This could happen directly in the reducer for those actions? 🤔
PocketStory(item, client) | ||
key(item.title) { | ||
PocketStory(item, client) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we use key()
here? Is it to avoid recomposition of PocketStory
unless the title changes? Why the title only though? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The title seemed like a small thing to check and enough to protect against recompositions. The stories are static in nature with no property changing.
Tested a bit now, don't see any recomposition. Same calls with or without key
. Will remove it.
oldestCategoryToDeselect?.let { | ||
homeStore.dispatch(HomeFragmentAction.DeselectPocketStoriesCategory(it.name)) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still think this should happen in the reducer since you are calculating a new state based on the current state here.
The review request from |
Pull Request checklist
To download an APK when reviewing a PR: