Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Commit

Permalink
For #21854 - Split the big ComposeView in 3 smaller ones
Browse files Browse the repository at this point in the history
This would shorten the time needed to layout all Pocket recommended stories
content in one go, though it may lead to shorten hiccups over a bigger period
of time.
  • Loading branch information
Mugurell authored and mergify[bot] committed Feb 9, 2022
1 parent 9b7e07a commit 5bd6f1c
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 104 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.fenix.home.pocket

import android.view.View
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.compose.SectionHeader
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.theme.FirefoxTheme

internal const val POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT = 8

/**
* [RecyclerView.ViewHolder] for displaying the list of [PocketRecommendedStoriesCategory]s from [HomeFragmentStore].
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
* @param store [HomeFragmentStore] containing the list of Pocket stories categories to be displayed.
* @param interactor [PocketStoriesInteractor] callback for user interaction.
*/
class PocketCategoriesViewHolder(
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
private val interactor: PocketStoriesInteractor
) : ComposeViewHolder(composeView, viewLifecycleOwner) {

@Composable
override fun Content() {
val horizontalPadding =
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)

val categories = store
.observeAsComposableState { state -> state.pocketStoriesCategories }.value
val categoriesSelections = store
.observeAsComposableState { state -> state.pocketStoriesCategoriesSelections }.value

Column {
Spacer(Modifier.height(24.dp))

PocketTopics(
categories = categories ?: emptyList(),
categoriesSelections = categoriesSelections ?: emptyList(),
onCategoryClick = interactor::onCategoryClicked
)
}
}

companion object {
val LAYOUT_ID = View.generateViewId()
}
}

@Composable
private fun PocketTopics(
categories: List<PocketRecommendedStoriesCategory> = emptyList(),
categoriesSelections: List<PocketRecommendedStoriesSelectedCategory> = emptyList(),
onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit
) {
Column {
SectionHeader(
text = stringResource(R.string.pocket_stories_categories_header),
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(align = Alignment.Top)
)

Spacer(Modifier.height(17.dp))

PocketStoriesCategories(
categories = categories,
selections = categoriesSelections,
onCategoryClick = onCategoryClick,
modifier = Modifier
.fillMaxWidth()
)
}
}

@Composable
@Preview
private fun PocketCategoriesViewHolderPreview() {
FirefoxTheme {
PocketTopics(
categories = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
.split(" ")
.map { PocketRecommendedStoriesCategory(it) },
categoriesSelections = emptyList(),
onCategoryClick = {}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

@file:Suppress("MagicNumber")

package org.mozilla.fenix.home.pocket

import android.view.View
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.theme.FirefoxTheme

/**
* [RecyclerView.ViewHolder] for displaying the Pocket feature header.
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
* @param interactor [PocketStoriesInteractor] callback for user interaction.
*/
class PocketRecommendationsHeaderViewHolder(
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val interactor: PocketStoriesInteractor
) : ComposeViewHolder(composeView, viewLifecycleOwner) {

@Composable
override fun Content() {
val horizontalPadding =
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)

Column {
Spacer(Modifier.height(24.dp))

PoweredByPocketHeader(
interactor::onLearnMoreClicked,
modifier = Modifier.fillMaxWidth()
)
}
}

companion object {
val LAYOUT_ID = View.generateViewId()
}
}

@Composable
@Preview
fun PocketRecommendationsFooterViewHolderPreview() {
FirefoxTheme {
PoweredByPocketHeader({})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ fun PoweredByPocketHeader(
Row(
Modifier
.fillMaxWidth()
.semantics(mergeDescendants = true) { },
.semantics(mergeDescendants = true) {},
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Expand Down Expand Up @@ -249,20 +249,20 @@ private fun PocketStoriesComposablesPreview() {
stories = getFakePocketStories(8),
contentPadding = 0.dp,
onStoryClicked = { _, _ -> },
onDiscoverMoreClicked = { }
onDiscoverMoreClicked = {}
)
Spacer(Modifier.height(10.dp))

PocketStoriesCategories(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor".split(" ").map {
PocketRecommendedStoriesCategory(it)
},
emptyList(),
{ }
categories = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
.split(" ")
.map { PocketRecommendedStoriesCategory(it) },
selections = emptyList(),
onCategoryClick = {}
)
Spacer(Modifier.height(10.dp))

PoweredByPocketHeader({ })
PoweredByPocketHeader({})
}
}
}
Expand All @@ -273,7 +273,7 @@ private class PocketStoryProvider : PreviewParameterProvider<PocketRecommendedSt
override val count = 8
}

private fun getFakePocketStories(limit: Int = 1): List<PocketRecommendedStory> {
internal fun getFakePocketStories(limit: Int = 1): List<PocketRecommendedStory> {
return mutableListOf<PocketRecommendedStory>().apply {
for (index in 0 until limit) {
val randomNumber = Random.nextInt(0, 10)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,18 @@ internal class DefaultPocketStoriesController(
)
}

override fun handleStoryClicked(storyClicked: PocketRecommendedStory, storyPosition: Pair<Int, Int>) {
override fun handleStoryClicked(
storyClicked: PocketRecommendedStory,
storyPosition: Pair<Int, Int>
) {
dismissSearchDialogIfDisplayed()
homeActivity.openToBrowserAndLoad(storyClicked.url, true, BrowserDirection.FromHome)
metrics.track(Event.PocketHomeRecsStoryClicked(storyClicked.timesShown.inc(), storyPosition))
metrics.track(
Event.PocketHomeRecsStoryClicked(
storyClicked.timesShown.inc(),
storyPosition
)
)
}

override fun handleLearnMoreClicked(link: String) {
Expand Down
Loading

0 comments on commit 5bd6f1c

Please sign in to comment.