1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 package org.mozilla.fenix.home.pocket
7 import android.view.View
8 import androidx.compose.foundation.layout.Column
9 import androidx.compose.foundation.layout.Spacer
10 import androidx.compose.foundation.layout.fillMaxWidth
11 import androidx.compose.foundation.layout.height
12 import androidx.compose.foundation.layout.wrapContentHeight
13 import androidx.compose.runtime.Composable
14 import androidx.compose.ui.Alignment
15 import androidx.compose.ui.Modifier
16 import androidx.compose.ui.platform.ComposeView
17 import androidx.compose.ui.res.stringResource
18 import androidx.compose.ui.tooling.preview.Preview
19 import androidx.compose.ui.unit.dp
20 import androidx.lifecycle.LifecycleOwner
21 import androidx.recyclerview.widget.RecyclerView
22 import mozilla.components.lib.state.ext.observeAsComposableState
23 import org.mozilla.fenix.R
24 import org.mozilla.fenix.compose.ComposeViewHolder
25 import org.mozilla.fenix.compose.SectionHeader
26 import org.mozilla.fenix.home.HomeFragmentStore
27 import org.mozilla.fenix.theme.FirefoxTheme
29 internal const val POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT = 8
32 * [RecyclerView.ViewHolder] for displaying the list of [PocketRecommendedStoriesCategory]s from [HomeFragmentStore].
34 * @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
35 * @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
36 * @param store [HomeFragmentStore] containing the list of Pocket stories categories to be displayed.
37 * @param interactor [PocketStoriesInteractor] callback for user interaction.
39 class PocketCategoriesViewHolder(
40 composeView: ComposeView,
41 viewLifecycleOwner: LifecycleOwner,
42 private val store: HomeFragmentStore,
43 private val interactor: PocketStoriesInteractor
44 ) : ComposeViewHolder(composeView, viewLifecycleOwner) {
47 override fun Content() {
48 val horizontalPadding =
49 composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
50 composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)
52 val categories = store
53 .observeAsComposableState { state -> state.pocketStoriesCategories }.value
54 val categoriesSelections = store
55 .observeAsComposableState { state -> state.pocketStoriesCategoriesSelections }.value
58 Spacer(Modifier.height(24.dp))
61 categories = categories ?: emptyList(),
62 categoriesSelections = categoriesSelections ?: emptyList(),
63 onCategoryClick = interactor::onCategoryClicked
69 val LAYOUT_ID = View.generateViewId()
74 private fun PocketTopics(
75 categories: List<PocketRecommendedStoriesCategory> = emptyList(),
76 categoriesSelections: List<PocketRecommendedStoriesSelectedCategory> = emptyList(),
77 onCategoryClick: (PocketRecommendedStoriesCategory) -> Unit
81 text = stringResource(R.string.pocket_stories_categories_header),
84 .wrapContentHeight(align = Alignment.Top)
87 Spacer(Modifier.height(16.dp))
89 PocketStoriesCategories(
90 categories = categories,
91 selections = categoriesSelections,
92 onCategoryClick = onCategoryClick,
101 private fun PocketCategoriesViewHolderPreview() {
104 categories = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
106 .map { PocketRecommendedStoriesCategory(it) },
107 categoriesSelections = emptyList(),