mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-21 17:22:05 +00:00
feat: adding watched markers to remaining places
This commit is contained in:
parent
507d842098
commit
c18916e6e3
8 changed files with 82 additions and 18 deletions
|
|
@ -6,6 +6,7 @@ import androidx.compose.animation.fadeOut
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
|
@ -32,6 +33,7 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import com.nuvio.app.core.format.formatReleaseDateForDisplay
|
||||
|
|
@ -151,6 +153,20 @@ fun NuvioAnimatedWatchedBadge(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BoxScope.NuvioPosterWatchedOverlay(
|
||||
isWatched: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
padding: Dp = 6.dp,
|
||||
) {
|
||||
NuvioAnimatedWatchedBadge(
|
||||
isVisible = isWatched,
|
||||
modifier = modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(padding),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PosterSheetHeader(
|
||||
item: MetaPreview,
|
||||
|
|
|
|||
|
|
@ -179,12 +179,7 @@ fun NuvioPosterCard(
|
|||
}
|
||||
}
|
||||
|
||||
NuvioAnimatedWatchedBadge(
|
||||
isVisible = isWatched,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(6.dp),
|
||||
)
|
||||
NuvioPosterWatchedOverlay(isWatched = isWatched)
|
||||
}
|
||||
if (shouldShowTitleBelow) {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import com.nuvio.app.core.ui.NuvioNetworkOfflineCard
|
|||
import coil3.compose.AsyncImage
|
||||
import com.nuvio.app.core.format.formatReleaseDateForDisplay
|
||||
import com.nuvio.app.core.ui.NuvioBackButton
|
||||
import com.nuvio.app.core.ui.NuvioPosterWatchedOverlay
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
import com.nuvio.app.core.ui.posterCardClickable
|
||||
import com.nuvio.app.core.ui.nuvioSafeBottomPadding
|
||||
|
|
@ -55,6 +56,8 @@ import com.nuvio.app.features.home.MetaPreview
|
|||
import com.nuvio.app.features.home.HomeCatalogSettingsRepository
|
||||
import com.nuvio.app.features.home.PosterShape
|
||||
import com.nuvio.app.features.home.stableKey
|
||||
import com.nuvio.app.features.watched.WatchedRepository
|
||||
import com.nuvio.app.features.watching.application.WatchingState
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
|
@ -79,6 +82,10 @@ fun CatalogScreen(
|
|||
val homeCatalogSettingsUiState by HomeCatalogSettingsRepository.uiState.collectAsStateWithLifecycle()
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
||||
val watchedUiState by remember {
|
||||
WatchedRepository.ensureLoaded()
|
||||
WatchedRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val gridState = rememberLazyGridState()
|
||||
var headerHeightPx by remember { mutableIntStateOf(0) }
|
||||
var observedOfflineState by remember { mutableStateOf(false) }
|
||||
|
|
@ -187,6 +194,10 @@ fun CatalogScreen(
|
|||
item = item,
|
||||
cornerRadiusDp = posterCardStyle.cornerRadiusDp,
|
||||
hideLabels = posterCardStyle.hideLabelsEnabled,
|
||||
isWatched = WatchingState.isPosterWatched(
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
item = item,
|
||||
),
|
||||
onClick = onPosterClick?.let { { it(item) } },
|
||||
onLongClick = onPosterLongClick?.let { { it(item) } },
|
||||
)
|
||||
|
|
@ -258,6 +269,7 @@ private fun CatalogPosterTile(
|
|||
item: MetaPreview,
|
||||
cornerRadiusDp: Int,
|
||||
hideLabels: Boolean,
|
||||
isWatched: Boolean,
|
||||
onClick: (() -> Unit)? = null,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
) {
|
||||
|
|
@ -280,6 +292,7 @@ private fun CatalogPosterTile(
|
|||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
}
|
||||
NuvioPosterWatchedOverlay(isWatched = isWatched)
|
||||
}
|
||||
if (!hideLabels) {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ import com.nuvio.app.features.home.PosterShape
|
|||
import com.nuvio.app.features.home.canOpenCatalog
|
||||
import com.nuvio.app.features.home.stableKey
|
||||
import com.nuvio.app.features.home.components.HomeCatalogRowSection
|
||||
import com.nuvio.app.features.watched.WatchedRepository
|
||||
import com.nuvio.app.features.watching.application.WatchingState
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
|
@ -79,6 +81,10 @@ fun FolderDetailScreen(
|
|||
onPosterClick: (MetaPreview) -> Unit,
|
||||
) {
|
||||
val uiState by FolderDetailRepository.uiState.collectAsState()
|
||||
val watchedUiState by remember {
|
||||
WatchedRepository.ensureLoaded()
|
||||
WatchedRepository.uiState
|
||||
}.collectAsState()
|
||||
val folder = uiState.folder
|
||||
val coverImageUrl = folder?.coverImageUrl?.takeIf { it.isNotBlank() }
|
||||
val density = LocalDensity.current
|
||||
|
|
@ -160,18 +166,21 @@ fun FolderDetailScreen(
|
|||
when (uiState.viewMode) {
|
||||
FolderViewMode.TABBED_GRID -> TabbedGridContent(
|
||||
uiState = uiState,
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
modifier = Modifier.weight(1f).then(contentModifier),
|
||||
onTabSelected = { FolderDetailRepository.selectTab(it) },
|
||||
onPosterClick = onPosterClick,
|
||||
)
|
||||
FolderViewMode.ROWS -> RowsContent(
|
||||
uiState = uiState,
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
modifier = Modifier.weight(1f).then(contentModifier),
|
||||
onCatalogClick = onCatalogClick,
|
||||
onPosterClick = onPosterClick,
|
||||
)
|
||||
FolderViewMode.FOLLOW_LAYOUT -> RowsContent(
|
||||
uiState = uiState,
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
modifier = Modifier.weight(1f).then(contentModifier),
|
||||
onCatalogClick = onCatalogClick,
|
||||
onPosterClick = onPosterClick,
|
||||
|
|
@ -199,6 +208,7 @@ private fun FolderCoverImage(
|
|||
@Composable
|
||||
private fun TabbedGridContent(
|
||||
uiState: FolderDetailUiState,
|
||||
watchedKeys: Set<String>,
|
||||
modifier: Modifier = Modifier,
|
||||
onTabSelected: (Int) -> Unit,
|
||||
onPosterClick: (MetaPreview) -> Unit,
|
||||
|
|
@ -285,6 +295,10 @@ private fun TabbedGridContent(
|
|||
imageUrl = item.poster,
|
||||
shape = NuvioPosterShape.Poster,
|
||||
detailLine = item.releaseInfo,
|
||||
isWatched = WatchingState.isPosterWatched(
|
||||
watchedKeys = watchedKeys,
|
||||
item = item,
|
||||
),
|
||||
onClick = { onPosterClick(item) },
|
||||
)
|
||||
}
|
||||
|
|
@ -304,6 +318,7 @@ private fun TabbedGridContent(
|
|||
@Composable
|
||||
private fun RowsContent(
|
||||
uiState: FolderDetailUiState,
|
||||
watchedKeys: Set<String>,
|
||||
modifier: Modifier = Modifier,
|
||||
onCatalogClick: (HomeCatalogSection) -> Unit,
|
||||
onPosterClick: (MetaPreview) -> Unit,
|
||||
|
|
@ -340,6 +355,7 @@ private fun RowsContent(
|
|||
} else {
|
||||
null
|
||||
},
|
||||
watchedKeys = watchedKeys,
|
||||
onPosterClick = { onPosterClick(it) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.lerp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.request.ImageRequest
|
||||
|
|
@ -63,6 +64,7 @@ import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
|||
import com.nuvio.app.features.details.components.DetailPosterRailSection
|
||||
import com.nuvio.app.features.home.MetaPreview
|
||||
import com.nuvio.app.features.tmdb.TmdbMetadataService
|
||||
import com.nuvio.app.features.watched.WatchedRepository
|
||||
import com.nuvio.app.features.watchprogress.CurrentDateProvider
|
||||
import nuvio.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.getString
|
||||
|
|
@ -89,6 +91,10 @@ fun PersonDetailScreen(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var uiState by remember(personId) { mutableStateOf<PersonDetailUiState>(PersonDetailUiState.Loading) }
|
||||
val watchedUiState by remember {
|
||||
WatchedRepository.ensureLoaded()
|
||||
WatchedRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val resolvedAvatarTransitionKey = avatarTransitionKey ?: castAvatarSharedTransitionKey(personId)
|
||||
|
||||
LaunchedEffect(personId) {
|
||||
|
|
@ -127,6 +133,7 @@ fun PersonDetailScreen(
|
|||
)
|
||||
is PersonDetailUiState.Success -> PersonDetailContent(
|
||||
person = state.personDetail,
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
onOpenMeta = onOpenMeta,
|
||||
initialProfilePhoto = initialProfilePhoto,
|
||||
avatarTransitionKey = resolvedAvatarTransitionKey,
|
||||
|
|
@ -156,6 +163,7 @@ fun PersonDetailScreen(
|
|||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
private fun PersonDetailContent(
|
||||
person: PersonDetail,
|
||||
watchedKeys: Set<String>,
|
||||
onOpenMeta: (MetaPreview) -> Unit,
|
||||
initialProfilePhoto: String? = null,
|
||||
avatarTransitionKey: String,
|
||||
|
|
@ -274,7 +282,7 @@ private fun PersonDetailContent(
|
|||
DetailPosterRailSection(
|
||||
title = stringResource(Res.string.person_popular),
|
||||
items = popularCredits,
|
||||
watchedKeys = emptySet(),
|
||||
watchedKeys = watchedKeys,
|
||||
headerHorizontalPadding = 20.dp,
|
||||
onPosterClick = onOpenMeta,
|
||||
)
|
||||
|
|
@ -285,7 +293,7 @@ private fun PersonDetailContent(
|
|||
DetailPosterRailSection(
|
||||
title = stringResource(Res.string.person_latest),
|
||||
items = latestCredits,
|
||||
watchedKeys = emptySet(),
|
||||
watchedKeys = watchedKeys,
|
||||
headerHorizontalPadding = 20.dp,
|
||||
onPosterClick = onOpenMeta,
|
||||
)
|
||||
|
|
@ -296,7 +304,7 @@ private fun PersonDetailContent(
|
|||
DetailPosterRailSection(
|
||||
title = stringResource(Res.string.person_upcoming),
|
||||
items = upcomingCredits,
|
||||
watchedKeys = emptySet(),
|
||||
watchedKeys = watchedKeys,
|
||||
headerHorizontalPadding = 20.dp,
|
||||
onPosterClick = onOpenMeta,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil3.compose.AsyncImage
|
||||
import nuvio.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
|
@ -55,6 +56,7 @@ import com.nuvio.app.features.tmdb.TmdbEntityKind
|
|||
import com.nuvio.app.features.tmdb.TmdbEntityMediaType
|
||||
import com.nuvio.app.features.tmdb.TmdbEntityRailType
|
||||
import com.nuvio.app.features.tmdb.TmdbMetadataService
|
||||
import com.nuvio.app.features.watched.WatchedRepository
|
||||
|
||||
private sealed interface EntityBrowseUiState {
|
||||
data object Loading : EntityBrowseUiState
|
||||
|
|
@ -75,6 +77,10 @@ fun TmdbEntityBrowseScreen(
|
|||
var uiState by remember(entityKind, entityId) {
|
||||
mutableStateOf<EntityBrowseUiState>(EntityBrowseUiState.Loading)
|
||||
}
|
||||
val watchedUiState by remember {
|
||||
WatchedRepository.ensureLoaded()
|
||||
WatchedRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val loadFailedMessage = stringResource(Res.string.details_browse_load_failed, entityName)
|
||||
|
||||
LaunchedEffect(entityKind, entityId) {
|
||||
|
|
@ -106,6 +112,7 @@ fun TmdbEntityBrowseScreen(
|
|||
is EntityBrowseUiState.Success -> EntityBrowseContent(
|
||||
data = state.data,
|
||||
sourceType = sourceType,
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
onOpenMeta = onOpenMeta,
|
||||
)
|
||||
}
|
||||
|
|
@ -131,6 +138,7 @@ fun TmdbEntityBrowseScreen(
|
|||
private fun EntityBrowseContent(
|
||||
data: TmdbEntityBrowseData,
|
||||
sourceType: String,
|
||||
watchedKeys: Set<String>,
|
||||
onOpenMeta: (MetaPreview) -> Unit,
|
||||
) {
|
||||
val backgroundUrl = remember(data.rails, sourceType) {
|
||||
|
|
@ -208,7 +216,7 @@ private fun EntityBrowseContent(
|
|||
DetailPosterRailSection(
|
||||
title = railTitle,
|
||||
items = rail.items,
|
||||
watchedKeys = emptySet(),
|
||||
watchedKeys = watchedKeys,
|
||||
headerHorizontalPadding = 20.dp,
|
||||
onPosterClick = onOpenMeta,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import com.nuvio.app.features.home.components.HomeEmptyStateCard
|
|||
import com.nuvio.app.features.home.components.HomePosterCard
|
||||
import com.nuvio.app.features.home.components.HomeSkeletonRow
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import com.nuvio.app.features.watched.WatchedRepository
|
||||
import com.nuvio.app.features.watching.application.WatchingState
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -50,6 +52,10 @@ fun LibraryScreen(
|
|||
LibraryRepository.ensureLoaded()
|
||||
LibraryRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val watchedUiState by remember {
|
||||
WatchedRepository.ensureLoaded()
|
||||
WatchedRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
||||
var observedOfflineState by remember { mutableStateOf(false) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
|
@ -176,6 +182,7 @@ fun LibraryScreen(
|
|||
else -> {
|
||||
librarySections(
|
||||
sections = uiState.sections,
|
||||
watchedKeys = watchedUiState.watchedKeys,
|
||||
onPosterClick = onPosterClick,
|
||||
onSectionViewAllClick = onSectionViewAllClick,
|
||||
onPosterLongClick = onPosterLongClick,
|
||||
|
|
@ -187,6 +194,7 @@ fun LibraryScreen(
|
|||
|
||||
private fun LazyListScope.librarySections(
|
||||
sections: List<LibrarySection>,
|
||||
watchedKeys: Set<String>,
|
||||
onPosterClick: ((LibraryItem) -> Unit)?,
|
||||
onSectionViewAllClick: ((LibrarySection) -> Unit)?,
|
||||
onPosterLongClick: ((LibraryItem, LibrarySection) -> Unit)?,
|
||||
|
|
@ -209,8 +217,13 @@ private fun LazyListScope.librarySections(
|
|||
viewAllPillSize = NuvioViewAllPillSize.Compact,
|
||||
key = { item -> "${item.type}:${item.id}" },
|
||||
) { item ->
|
||||
val posterItem = item.toMetaPreview()
|
||||
HomePosterCard(
|
||||
item = item.toMetaPreview(),
|
||||
item = posterItem,
|
||||
isWatched = WatchingState.isPosterWatched(
|
||||
watchedKeys = watchedKeys,
|
||||
item = posterItem,
|
||||
),
|
||||
onClick = onPosterClick?.let { { it(item) } },
|
||||
onLongClick = onPosterLongClick?.let { { it(item, section) } },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,12 +50,12 @@ import coil3.compose.AsyncImage
|
|||
import com.nuvio.app.core.network.NetworkCondition
|
||||
import com.nuvio.app.core.format.formatReleaseDateForDisplay
|
||||
import com.nuvio.app.core.ui.NuvioNetworkOfflineCard
|
||||
import com.nuvio.app.core.ui.NuvioAnimatedWatchedBadge
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetActionRow
|
||||
import com.nuvio.app.core.ui.NuvioBottomSheetDivider
|
||||
import com.nuvio.app.core.ui.NuvioModalBottomSheet
|
||||
import com.nuvio.app.core.ui.dismissNuvioBottomSheet
|
||||
import com.nuvio.app.core.ui.nuvioSafeBottomPadding
|
||||
import com.nuvio.app.core.ui.NuvioPosterWatchedOverlay
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
import com.nuvio.app.core.ui.posterCardClickable
|
||||
import com.nuvio.app.features.home.MetaPreview
|
||||
|
|
@ -404,12 +404,7 @@ private fun DiscoverPosterTile(
|
|||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
}
|
||||
NuvioAnimatedWatchedBadge(
|
||||
isVisible = isWatched,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(6.dp),
|
||||
)
|
||||
NuvioPosterWatchedOverlay(isWatched = isWatched)
|
||||
}
|
||||
if (!hideLabels) {
|
||||
Text(
|
||||
|
|
|
|||
Loading…
Reference in a new issue