You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
5.2 KiB
Kotlin
127 lines
5.2 KiB
Kotlin
/* 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.Box
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.Spacer
|
|
import androidx.compose.foundation.layout.height
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.platform.ComposeView
|
|
import androidx.compose.ui.res.dimensionResource
|
|
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 mozilla.components.service.pocket.PocketStory.PocketRecommendedStory
|
|
import org.mozilla.fenix.R
|
|
import org.mozilla.fenix.components.components
|
|
import org.mozilla.fenix.compose.ComposeViewHolder
|
|
import org.mozilla.fenix.compose.home.HomeSectionHeader
|
|
import org.mozilla.fenix.theme.FirefoxTheme
|
|
import org.mozilla.fenix.wallpapers.WallpaperState
|
|
|
|
/**
|
|
* [RecyclerView.ViewHolder] for displaying the list of [PocketRecommendedStory]s from [AppStore].
|
|
*
|
|
* @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 PocketStoriesViewHolder(
|
|
composeView: ComposeView,
|
|
viewLifecycleOwner: LifecycleOwner,
|
|
private val interactor: PocketStoriesInteractor,
|
|
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
|
|
|
|
companion object {
|
|
val LAYOUT_ID = View.generateViewId()
|
|
}
|
|
|
|
@Composable
|
|
override fun Content() {
|
|
val horizontalPadding = dimensionResource(R.dimen.home_item_horizontal_margin)
|
|
|
|
val homeScreenReady = components.appStore
|
|
.observeAsComposableState { state -> state.firstFrameDrawn }.value ?: false
|
|
|
|
val stories = components.appStore
|
|
.observeAsComposableState { state -> state.pocketStories }.value
|
|
|
|
val wallpaperState = components.appStore
|
|
.observeAsComposableState { state -> state.wallpaperState }.value ?: WallpaperState.default
|
|
|
|
/* This was originally done to address this perf issue:
|
|
* https://github.com/mozilla-mobile/fenix/issues/25545 for details.
|
|
* It was determined that Pocket content was becoming available before the first frame was
|
|
* rendered more regularly. Including Pocket in the first render pass significantly
|
|
* increases time-to-render in lower-end devices. By waiting until the first frame has
|
|
* rendered, the perceived performance should increase since the app becomes active more
|
|
* quickly. This was intended as a workaround until the Compose upgrade was completed and a
|
|
* more robust solution could be investigated.
|
|
*/
|
|
if (!homeScreenReady) return
|
|
LaunchedEffect(stories) {
|
|
// We should report back when a certain story is actually being displayed.
|
|
// Cannot do it reliably so for now we'll just mass report everything as being displayed.
|
|
stories?.let {
|
|
// Only report here the impressions for recommended stories.
|
|
// Sponsored stories use a different API for more accurate tracking.
|
|
interactor.onStoriesShown(it.filterIsInstance<PocketRecommendedStory>())
|
|
}
|
|
}
|
|
|
|
Column(modifier = Modifier.padding(top = 72.dp)) {
|
|
// Simple wrapper to add horizontal padding to just the header while the stories have none.
|
|
Box(modifier = Modifier.padding(horizontal = horizontalPadding)) {
|
|
HomeSectionHeader(
|
|
headerText = stringResource(R.string.pocket_stories_header_1),
|
|
)
|
|
}
|
|
|
|
Spacer(Modifier.height(16.dp))
|
|
|
|
PocketStories(
|
|
stories = stories ?: emptyList(),
|
|
contentPadding = horizontalPadding,
|
|
backgroundColor = wallpaperState.wallpaperCardColor,
|
|
onStoryShown = interactor::onStoryShown,
|
|
onStoryClicked = interactor::onStoryClicked,
|
|
onDiscoverMoreClicked = interactor::onDiscoverMoreClicked,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
@Preview
|
|
fun PocketStoriesViewHolderPreview() {
|
|
FirefoxTheme {
|
|
Column {
|
|
HomeSectionHeader(
|
|
headerText = stringResource(R.string.pocket_stories_header_1),
|
|
)
|
|
|
|
Spacer(Modifier.height(16.dp))
|
|
|
|
@Suppress("MagicNumber")
|
|
PocketStories(
|
|
stories = getFakePocketStories(8),
|
|
contentPadding = 0.dp,
|
|
backgroundColor = FirefoxTheme.colors.layer2,
|
|
onStoryShown = { _, _ -> },
|
|
onStoryClicked = { _, _ -> },
|
|
onDiscoverMoreClicked = {},
|
|
)
|
|
}
|
|
}
|
|
}
|