|
|
@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid.feature.foryou
|
|
|
|
import android.app.Activity
|
|
|
|
import android.app.Activity
|
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
|
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
|
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
|
|
|
|
|
|
import androidx.compose.foundation.layout.Column
|
|
|
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
|
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
|
|
import androidx.compose.foundation.layout.Row
|
|
|
|
import androidx.compose.foundation.layout.Row
|
|
|
@ -37,14 +38,15 @@ import androidx.compose.foundation.layout.width
|
|
|
|
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
|
|
|
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
|
|
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
|
|
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
|
|
|
import androidx.compose.foundation.layout.wrapContentSize
|
|
|
|
import androidx.compose.foundation.layout.wrapContentSize
|
|
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
|
|
|
|
|
|
import androidx.compose.foundation.lazy.LazyListScope
|
|
|
|
import androidx.compose.foundation.lazy.LazyListScope
|
|
|
|
import androidx.compose.foundation.lazy.grid.GridCells
|
|
|
|
import androidx.compose.foundation.lazy.grid.GridCells
|
|
|
|
|
|
|
|
import androidx.compose.foundation.lazy.grid.GridCells.Adaptive
|
|
|
|
|
|
|
|
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
|
|
|
|
|
|
|
import androidx.compose.foundation.lazy.grid.LazyGridScope
|
|
|
|
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
|
|
|
|
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
|
|
|
|
|
|
|
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
|
|
|
import androidx.compose.foundation.lazy.grid.items
|
|
|
|
import androidx.compose.foundation.lazy.grid.items
|
|
|
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
|
|
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
|
|
|
import androidx.compose.foundation.lazy.items
|
|
|
|
|
|
|
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
|
|
|
|
|
|
import androidx.compose.foundation.shape.CornerSize
|
|
|
|
import androidx.compose.foundation.shape.CornerSize
|
|
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
|
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
|
@ -54,9 +56,6 @@ import androidx.compose.material3.Scaffold
|
|
|
|
import androidx.compose.material3.Surface
|
|
|
|
import androidx.compose.material3.Surface
|
|
|
|
import androidx.compose.material3.Text
|
|
|
|
import androidx.compose.material3.Text
|
|
|
|
import androidx.compose.material3.TopAppBarDefaults
|
|
|
|
import androidx.compose.material3.TopAppBarDefaults
|
|
|
|
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
|
|
|
|
|
|
|
|
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
|
|
|
|
|
|
|
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
|
|
|
|
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
@ -70,7 +69,6 @@ import androidx.compose.ui.res.painterResource
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
import androidx.compose.ui.text.style.TextAlign
|
|
|
|
import androidx.compose.ui.text.style.TextAlign
|
|
|
|
import androidx.compose.ui.tooling.preview.Preview
|
|
|
|
import androidx.compose.ui.tooling.preview.Preview
|
|
|
|
import androidx.compose.ui.unit.DpSize
|
|
|
|
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import androidx.compose.ui.unit.max
|
|
|
|
import androidx.compose.ui.unit.max
|
|
|
|
import androidx.compose.ui.unit.sp
|
|
|
|
import androidx.compose.ui.unit.sp
|
|
|
@ -93,36 +91,32 @@ import com.google.samples.apps.nowinandroid.core.model.data.SaveableNewsResource
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.previewAuthors
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.previewAuthors
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.previewNewsResources
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.previewTopics
|
|
|
|
import com.google.samples.apps.nowinandroid.core.model.data.previewTopics
|
|
|
|
import com.google.samples.apps.nowinandroid.core.ui.NewsFeed
|
|
|
|
|
|
|
|
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
|
|
|
|
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
|
|
|
|
import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank
|
|
|
|
import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank
|
|
|
|
import kotlin.math.floor
|
|
|
|
import com.google.samples.apps.nowinandroid.core.ui.newsFeed
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalLifecycleComposeApi::class)
|
|
|
|
@OptIn(ExperimentalLifecycleComposeApi::class)
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
fun ForYouRoute(
|
|
|
|
fun ForYouRoute(
|
|
|
|
windowSizeClass: WindowSizeClass,
|
|
|
|
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
modifier: Modifier = Modifier,
|
|
|
|
viewModel: ForYouViewModel = hiltViewModel()
|
|
|
|
viewModel: ForYouViewModel = hiltViewModel()
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
val interestsSelectionState by viewModel.interestsSelectionState.collectAsStateWithLifecycle()
|
|
|
|
val interestsSelectionState by viewModel.interestsSelectionState.collectAsStateWithLifecycle()
|
|
|
|
val feedState by viewModel.feedState.collectAsStateWithLifecycle()
|
|
|
|
val feedState by viewModel.feedState.collectAsStateWithLifecycle()
|
|
|
|
ForYouScreen(
|
|
|
|
ForYouScreen(
|
|
|
|
windowSizeClass = windowSizeClass,
|
|
|
|
|
|
|
|
modifier = modifier,
|
|
|
|
|
|
|
|
interestsSelectionState = interestsSelectionState,
|
|
|
|
interestsSelectionState = interestsSelectionState,
|
|
|
|
feedState = feedState,
|
|
|
|
feedState = feedState,
|
|
|
|
onTopicCheckedChanged = viewModel::updateTopicSelection,
|
|
|
|
onTopicCheckedChanged = viewModel::updateTopicSelection,
|
|
|
|
onAuthorCheckedChanged = viewModel::updateAuthorSelection,
|
|
|
|
onAuthorCheckedChanged = viewModel::updateAuthorSelection,
|
|
|
|
saveFollowedTopics = viewModel::saveFollowedInterests,
|
|
|
|
saveFollowedTopics = viewModel::saveFollowedInterests,
|
|
|
|
onNewsResourcesCheckedChanged = viewModel::updateNewsResourceSaved
|
|
|
|
onNewsResourcesCheckedChanged = viewModel::updateNewsResourceSaved,
|
|
|
|
|
|
|
|
modifier = modifier
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
|
|
|
@Composable
|
|
|
|
@Composable
|
|
|
|
fun ForYouScreen(
|
|
|
|
fun ForYouScreen(
|
|
|
|
windowSizeClass: WindowSizeClass,
|
|
|
|
|
|
|
|
interestsSelectionState: ForYouInterestsSelectionUiState,
|
|
|
|
interestsSelectionState: ForYouInterestsSelectionUiState,
|
|
|
|
feedState: NewsFeedUiState,
|
|
|
|
feedState: NewsFeedUiState,
|
|
|
|
onTopicCheckedChanged: (String, Boolean) -> Unit,
|
|
|
|
onTopicCheckedChanged: (String, Boolean) -> Unit,
|
|
|
@ -154,73 +148,63 @@ fun ForYouScreen(
|
|
|
|
},
|
|
|
|
},
|
|
|
|
containerColor = Color.Transparent
|
|
|
|
containerColor = Color.Transparent
|
|
|
|
) { innerPadding ->
|
|
|
|
) { innerPadding ->
|
|
|
|
// TODO: Replace with `LazyVerticalGrid` when blocking bugs are fixed:
|
|
|
|
// Workaround to call Activity.reportFullyDrawn from Jetpack Compose.
|
|
|
|
// https://issuetracker.google.com/issues/230514914
|
|
|
|
// This code should be called when the UI is ready for use
|
|
|
|
// https://issuetracker.google.com/issues/231320714
|
|
|
|
// and relates to Time To Full Display.
|
|
|
|
BoxWithConstraints(
|
|
|
|
val interestsLoaded =
|
|
|
|
modifier = modifier
|
|
|
|
interestsSelectionState !is ForYouInterestsSelectionUiState.Loading
|
|
|
|
.padding(innerPadding)
|
|
|
|
val feedLoaded = feedState !is NewsFeedUiState.Loading
|
|
|
|
.consumedWindowInsets(innerPadding)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
val numberOfColumns = when (windowSizeClass.widthSizeClass) {
|
|
|
|
|
|
|
|
WindowWidthSizeClass.Compact, WindowWidthSizeClass.Medium -> 1
|
|
|
|
|
|
|
|
else -> floor(maxWidth / 300.dp).toInt().coerceAtLeast(1)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Workaround to call Activity.reportFullyDrawn from Jetpack Compose.
|
|
|
|
|
|
|
|
// This code should be called when the UI is ready for use
|
|
|
|
|
|
|
|
// and relates to Time To Full Display.
|
|
|
|
|
|
|
|
val interestsLoaded =
|
|
|
|
|
|
|
|
interestsSelectionState !is ForYouInterestsSelectionUiState.Loading
|
|
|
|
|
|
|
|
val feedLoaded = feedState !is NewsFeedUiState.Loading
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (interestsLoaded && feedLoaded) {
|
|
|
|
if (interestsLoaded && feedLoaded) {
|
|
|
|
val localView = LocalView.current
|
|
|
|
val localView = LocalView.current
|
|
|
|
// We use Unit to call reportFullyDrawn only on the first recomposition,
|
|
|
|
// We use Unit to call reportFullyDrawn only on the first recomposition,
|
|
|
|
// however it will be called again if this composable goes out of scope.
|
|
|
|
// however it will be called again if this composable goes out of scope.
|
|
|
|
// Activity.reportFullyDrawn() has its own check for this
|
|
|
|
// Activity.reportFullyDrawn() has its own check for this
|
|
|
|
// and is safe to call multiple times though.
|
|
|
|
// and is safe to call multiple times though.
|
|
|
|
LaunchedEffect(Unit) {
|
|
|
|
LaunchedEffect(Unit) {
|
|
|
|
// We're leveraging the fact, that the current view is directly set as content of Activity.
|
|
|
|
// We're leveraging the fact, that the current view is directly set as content of Activity.
|
|
|
|
val activity = localView.context as? Activity ?: return@LaunchedEffect
|
|
|
|
val activity = localView.context as? Activity ?: return@LaunchedEffect
|
|
|
|
// To be sure not to call in the middle of a frame draw.
|
|
|
|
// To be sure not to call in the middle of a frame draw.
|
|
|
|
localView.doOnPreDraw { activity.reportFullyDrawn() }
|
|
|
|
localView.doOnPreDraw { activity.reportFullyDrawn() }
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
val tag = "forYou:feed"
|
|
|
|
val tag = "forYou:feed"
|
|
|
|
|
|
|
|
|
|
|
|
val lazyListState = rememberLazyListState()
|
|
|
|
val lazyGridState = rememberLazyGridState()
|
|
|
|
TrackScrollJank(scrollableState = lazyListState, stateName = tag)
|
|
|
|
TrackScrollJank(scrollableState = lazyGridState, stateName = tag)
|
|
|
|
|
|
|
|
|
|
|
|
LazyColumn(
|
|
|
|
LazyVerticalGrid(
|
|
|
|
modifier = Modifier
|
|
|
|
columns = Adaptive(300.dp),
|
|
|
|
.fillMaxSize()
|
|
|
|
contentPadding = PaddingValues(16.dp),
|
|
|
|
.testTag(tag),
|
|
|
|
horizontalArrangement = Arrangement.spacedBy(32.dp),
|
|
|
|
state = lazyListState,
|
|
|
|
verticalArrangement = Arrangement.spacedBy(24.dp),
|
|
|
|
) {
|
|
|
|
modifier = modifier
|
|
|
|
InterestsSelection(
|
|
|
|
.padding(innerPadding)
|
|
|
|
interestsSelectionState = interestsSelectionState,
|
|
|
|
.consumedWindowInsets(innerPadding)
|
|
|
|
showLoadingUIIfLoading = true,
|
|
|
|
.fillMaxSize()
|
|
|
|
onAuthorCheckedChanged = onAuthorCheckedChanged,
|
|
|
|
.testTag("forYou:feed"),
|
|
|
|
onTopicCheckedChanged = onTopicCheckedChanged,
|
|
|
|
state = lazyGridState
|
|
|
|
saveFollowedTopics = saveFollowedTopics
|
|
|
|
) {
|
|
|
|
)
|
|
|
|
interestsSelection(
|
|
|
|
|
|
|
|
interestsSelectionState = interestsSelectionState,
|
|
|
|
|
|
|
|
onAuthorCheckedChanged = onAuthorCheckedChanged,
|
|
|
|
|
|
|
|
onTopicCheckedChanged = onTopicCheckedChanged,
|
|
|
|
|
|
|
|
saveFollowedTopics = saveFollowedTopics
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
NewsFeed(
|
|
|
|
newsFeed(
|
|
|
|
feedState = feedState,
|
|
|
|
feedState = feedState,
|
|
|
|
// Avoid showing a second loading wheel if we already are for the interests
|
|
|
|
// Avoid showing a second loading wheel if we already are for the interests
|
|
|
|
// selection
|
|
|
|
// selection
|
|
|
|
showLoadingUIIfLoading =
|
|
|
|
showLoadingUIIfLoading =
|
|
|
|
interestsSelectionState !is ForYouInterestsSelectionUiState.Loading,
|
|
|
|
interestsSelectionState !is ForYouInterestsSelectionUiState.Loading,
|
|
|
|
numberOfColumns = numberOfColumns,
|
|
|
|
onNewsResourcesCheckedChanged = onNewsResourcesCheckedChanged,
|
|
|
|
onNewsResourcesCheckedChanged = onNewsResourcesCheckedChanged,
|
|
|
|
loadingContentDescription = R.string.for_you_loading
|
|
|
|
loadingContentDescription = R.string.for_you_loading
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
item {
|
|
|
|
item(span = { GridItemSpan(maxLineSpan) }) {
|
|
|
|
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
|
|
|
|
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -231,85 +215,76 @@ fun ForYouScreen(
|
|
|
|
* An extension on [LazyListScope] defining the interests selection portion of the for you screen.
|
|
|
|
* An extension on [LazyListScope] defining the interests selection portion of the for you screen.
|
|
|
|
* Depending on the [interestsSelectionState], this might emit no items.
|
|
|
|
* Depending on the [interestsSelectionState], this might emit no items.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param showLoadingUIIfLoading if true, show a visual indication of loading if the
|
|
|
|
* @param showLoaderWhenLoading if true, show a visual indication of loading if the
|
|
|
|
* [interestsSelectionState] is loading. This is controllable to permit du-duplicating loading
|
|
|
|
* [interestsSelectionState] is loading. This is controllable to permit du-duplicating loading
|
|
|
|
* states.
|
|
|
|
* states.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private fun LazyListScope.InterestsSelection(
|
|
|
|
private fun LazyGridScope.interestsSelection(
|
|
|
|
interestsSelectionState: ForYouInterestsSelectionUiState,
|
|
|
|
interestsSelectionState: ForYouInterestsSelectionUiState,
|
|
|
|
showLoadingUIIfLoading: Boolean,
|
|
|
|
|
|
|
|
onAuthorCheckedChanged: (String, Boolean) -> Unit,
|
|
|
|
onAuthorCheckedChanged: (String, Boolean) -> Unit,
|
|
|
|
onTopicCheckedChanged: (String, Boolean) -> Unit,
|
|
|
|
onTopicCheckedChanged: (String, Boolean) -> Unit,
|
|
|
|
saveFollowedTopics: () -> Unit
|
|
|
|
saveFollowedTopics: () -> Unit
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
when (interestsSelectionState) {
|
|
|
|
when (interestsSelectionState) {
|
|
|
|
ForYouInterestsSelectionUiState.Loading -> {
|
|
|
|
ForYouInterestsSelectionUiState.Loading -> {
|
|
|
|
if (showLoadingUIIfLoading) {
|
|
|
|
item(span = { GridItemSpan(maxLineSpan) }) {
|
|
|
|
item {
|
|
|
|
NiaLoadingWheel(
|
|
|
|
NiaLoadingWheel(
|
|
|
|
|
|
|
|
modifier = Modifier
|
|
|
|
|
|
|
|
.fillMaxWidth()
|
|
|
|
|
|
|
|
.wrapContentSize()
|
|
|
|
|
|
|
|
.testTag("forYou:loading"),
|
|
|
|
|
|
|
|
contentDesc = stringResource(id = R.string.for_you_loading),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ForYouInterestsSelectionUiState.NoInterestsSelection -> Unit
|
|
|
|
|
|
|
|
is ForYouInterestsSelectionUiState.WithInterestsSelection -> {
|
|
|
|
|
|
|
|
item {
|
|
|
|
|
|
|
|
Text(
|
|
|
|
|
|
|
|
text = stringResource(R.string.onboarding_guidance_title),
|
|
|
|
|
|
|
|
textAlign = TextAlign.Center,
|
|
|
|
|
|
|
|
modifier = Modifier
|
|
|
|
modifier = Modifier
|
|
|
|
.fillMaxWidth()
|
|
|
|
.fillMaxWidth()
|
|
|
|
.padding(top = 24.dp),
|
|
|
|
.wrapContentSize()
|
|
|
|
style = MaterialTheme.typography.titleMedium
|
|
|
|
.testTag("forYou:loading"),
|
|
|
|
|
|
|
|
contentDesc = stringResource(id = R.string.for_you_loading),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
item {
|
|
|
|
}
|
|
|
|
Text(
|
|
|
|
ForYouInterestsSelectionUiState.NoInterestsSelection -> Unit
|
|
|
|
text = stringResource(R.string.onboarding_guidance_subtitle),
|
|
|
|
is ForYouInterestsSelectionUiState.WithInterestsSelection -> {
|
|
|
|
modifier = Modifier
|
|
|
|
item(span = { GridItemSpan(maxLineSpan) }) {
|
|
|
|
.fillMaxWidth()
|
|
|
|
Column {
|
|
|
|
.padding(top = 8.dp, start = 16.dp, end = 16.dp),
|
|
|
|
Text(
|
|
|
|
textAlign = TextAlign.Center,
|
|
|
|
text = stringResource(R.string.onboarding_guidance_title),
|
|
|
|
style = MaterialTheme.typography.bodyMedium
|
|
|
|
textAlign = TextAlign.Center,
|
|
|
|
)
|
|
|
|
modifier = Modifier
|
|
|
|
}
|
|
|
|
.fillMaxWidth()
|
|
|
|
item {
|
|
|
|
.padding(top = 24.dp),
|
|
|
|
AuthorsCarousel(
|
|
|
|
style = MaterialTheme.typography.titleMedium
|
|
|
|
authors = interestsSelectionState.authors,
|
|
|
|
)
|
|
|
|
onAuthorClick = onAuthorCheckedChanged,
|
|
|
|
Text(
|
|
|
|
modifier = Modifier
|
|
|
|
text = stringResource(R.string.onboarding_guidance_subtitle),
|
|
|
|
.fillMaxWidth()
|
|
|
|
modifier = Modifier
|
|
|
|
.padding(vertical = 8.dp)
|
|
|
|
.fillMaxWidth()
|
|
|
|
)
|
|
|
|
.padding(top = 8.dp, start = 16.dp, end = 16.dp),
|
|
|
|
}
|
|
|
|
textAlign = TextAlign.Center,
|
|
|
|
item {
|
|
|
|
style = MaterialTheme.typography.bodyMedium
|
|
|
|
TopicSelection(
|
|
|
|
)
|
|
|
|
interestsSelectionState,
|
|
|
|
AuthorsCarousel(
|
|
|
|
onTopicCheckedChanged,
|
|
|
|
authors = interestsSelectionState.authors,
|
|
|
|
Modifier.padding(bottom = 8.dp)
|
|
|
|
onAuthorClick = onAuthorCheckedChanged,
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
item {
|
|
|
|
|
|
|
|
// Done button
|
|
|
|
|
|
|
|
Row(
|
|
|
|
|
|
|
|
horizontalArrangement = Arrangement.Center,
|
|
|
|
|
|
|
|
modifier = Modifier.fillMaxWidth()
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
NiaFilledButton(
|
|
|
|
|
|
|
|
onClick = saveFollowedTopics,
|
|
|
|
|
|
|
|
enabled = interestsSelectionState.canSaveInterests,
|
|
|
|
|
|
|
|
modifier = Modifier
|
|
|
|
modifier = Modifier
|
|
|
|
.padding(horizontal = 40.dp)
|
|
|
|
.fillMaxWidth()
|
|
|
|
.width(364.dp)
|
|
|
|
.padding(vertical = 8.dp)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
TopicSelection(
|
|
|
|
|
|
|
|
interestsSelectionState,
|
|
|
|
|
|
|
|
onTopicCheckedChanged,
|
|
|
|
|
|
|
|
Modifier.padding(bottom = 8.dp)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
// Done button
|
|
|
|
|
|
|
|
Row(
|
|
|
|
|
|
|
|
horizontalArrangement = Arrangement.Center,
|
|
|
|
|
|
|
|
modifier = Modifier.fillMaxWidth()
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
Text(
|
|
|
|
NiaFilledButton(
|
|
|
|
text = stringResource(R.string.done)
|
|
|
|
onClick = saveFollowedTopics,
|
|
|
|
)
|
|
|
|
enabled = interestsSelectionState.canSaveInterests,
|
|
|
|
|
|
|
|
modifier = Modifier
|
|
|
|
|
|
|
|
.padding(horizontal = 40.dp)
|
|
|
|
|
|
|
|
.width(364.dp)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
Text(
|
|
|
|
|
|
|
|
text = stringResource(R.string.done)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -428,7 +403,6 @@ fun TopicIcon(
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
|
|
|
|
|
|
|
@Preview(name = "phone", device = "spec:shape=Normal,width=360,height=640,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "phone", device = "spec:shape=Normal,width=360,height=640,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "landscape", device = "spec:shape=Normal,width=640,height=360,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "landscape", device = "spec:shape=Normal,width=640,height=360,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "foldable", device = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "foldable", device = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480")
|
|
|
@ -438,7 +412,6 @@ fun ForYouScreenPopulatedFeed() {
|
|
|
|
BoxWithConstraints {
|
|
|
|
BoxWithConstraints {
|
|
|
|
NiaTheme {
|
|
|
|
NiaTheme {
|
|
|
|
ForYouScreen(
|
|
|
|
ForYouScreen(
|
|
|
|
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)),
|
|
|
|
|
|
|
|
interestsSelectionState = ForYouInterestsSelectionUiState.NoInterestsSelection,
|
|
|
|
interestsSelectionState = ForYouInterestsSelectionUiState.NoInterestsSelection,
|
|
|
|
feedState = NewsFeedUiState.Success(
|
|
|
|
feedState = NewsFeedUiState.Success(
|
|
|
|
feed = previewNewsResources.map {
|
|
|
|
feed = previewNewsResources.map {
|
|
|
@ -454,7 +427,6 @@ fun ForYouScreenPopulatedFeed() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
|
|
|
|
|
|
|
@Preview(name = "phone", device = "spec:shape=Normal,width=360,height=640,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "phone", device = "spec:shape=Normal,width=360,height=640,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "landscape", device = "spec:shape=Normal,width=640,height=360,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "landscape", device = "spec:shape=Normal,width=640,height=360,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "foldable", device = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "foldable", device = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480")
|
|
|
@ -464,7 +436,6 @@ fun ForYouScreenTopicSelection() {
|
|
|
|
BoxWithConstraints {
|
|
|
|
BoxWithConstraints {
|
|
|
|
NiaTheme {
|
|
|
|
NiaTheme {
|
|
|
|
ForYouScreen(
|
|
|
|
ForYouScreen(
|
|
|
|
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)),
|
|
|
|
|
|
|
|
interestsSelectionState = ForYouInterestsSelectionUiState.WithInterestsSelection(
|
|
|
|
interestsSelectionState = ForYouInterestsSelectionUiState.WithInterestsSelection(
|
|
|
|
topics = previewTopics.map { FollowableTopic(it, false) },
|
|
|
|
topics = previewTopics.map { FollowableTopic(it, false) },
|
|
|
|
authors = previewAuthors.map { FollowableAuthor(it, false) }
|
|
|
|
authors = previewAuthors.map { FollowableAuthor(it, false) }
|
|
|
@ -474,8 +445,8 @@ fun ForYouScreenTopicSelection() {
|
|
|
|
SaveableNewsResource(it, false)
|
|
|
|
SaveableNewsResource(it, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
),
|
|
|
|
),
|
|
|
|
onAuthorCheckedChanged = { _, _ -> },
|
|
|
|
|
|
|
|
onTopicCheckedChanged = { _, _ -> },
|
|
|
|
onTopicCheckedChanged = { _, _ -> },
|
|
|
|
|
|
|
|
onAuthorCheckedChanged = { _, _ -> },
|
|
|
|
saveFollowedTopics = {},
|
|
|
|
saveFollowedTopics = {},
|
|
|
|
onNewsResourcesCheckedChanged = { _, _ -> }
|
|
|
|
onNewsResourcesCheckedChanged = { _, _ -> }
|
|
|
|
)
|
|
|
|
)
|
|
|
@ -483,7 +454,6 @@ fun ForYouScreenTopicSelection() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
|
|
|
|
|
|
|
@Preview(name = "phone", device = "spec:shape=Normal,width=360,height=640,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "phone", device = "spec:shape=Normal,width=360,height=640,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "landscape", device = "spec:shape=Normal,width=640,height=360,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "landscape", device = "spec:shape=Normal,width=640,height=360,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "foldable", device = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480")
|
|
|
|
@Preview(name = "foldable", device = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480")
|
|
|
@ -493,7 +463,6 @@ fun ForYouScreenLoading() {
|
|
|
|
BoxWithConstraints {
|
|
|
|
BoxWithConstraints {
|
|
|
|
NiaTheme {
|
|
|
|
NiaTheme {
|
|
|
|
ForYouScreen(
|
|
|
|
ForYouScreen(
|
|
|
|
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(maxWidth, maxHeight)),
|
|
|
|
|
|
|
|
interestsSelectionState = ForYouInterestsSelectionUiState.Loading,
|
|
|
|
interestsSelectionState = ForYouInterestsSelectionUiState.Loading,
|
|
|
|
feedState = NewsFeedUiState.Loading,
|
|
|
|
feedState = NewsFeedUiState.Loading,
|
|
|
|
onTopicCheckedChanged = { _, _ -> },
|
|
|
|
onTopicCheckedChanged = { _, _ -> },
|
|
|
|