mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
parent
82111b99e4
commit
bb19ba316b
10 changed files with 80 additions and 16 deletions
|
|
@ -12,6 +12,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
|||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||
import com.github.damontecres.wholphin.ui.main.HomeRow
|
||||
import com.github.damontecres.wholphin.ui.main.HomeSection
|
||||
|
|
@ -114,7 +115,7 @@ class RecommendedMovieViewModel
|
|||
HomeRow(HomeSection.LATEST_MEDIA, recentlyAddedItems, "Recently Added"),
|
||||
HomeRow(HomeSection.NONE, suggestedItems, "Suggestions"),
|
||||
HomeRow(HomeSection.NONE, unwatchedTopRatedItems, "Top Rated Unwatched"),
|
||||
)
|
||||
).filter { it.items.isNotEmpty() }
|
||||
withContext(Dispatchers.Main) {
|
||||
this@RecommendedMovieViewModel.rows.value = homeRows
|
||||
loading.value = LoadingState.Success
|
||||
|
|
@ -131,6 +132,7 @@ fun RecommendedMovie(
|
|||
preferences: UserPreferences,
|
||||
parentId: UUID,
|
||||
onClickItem: (BaseItem) -> Unit,
|
||||
onFocusPosition: (RowColumn) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: RecommendedMovieViewModel = hiltViewModel(),
|
||||
) {
|
||||
|
|
@ -151,6 +153,7 @@ fun RecommendedMovie(
|
|||
HomePageContent(
|
||||
homeRows = rows,
|
||||
onClickItem = onClickItem,
|
||||
onFocusPosition = onFocusPosition,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
|||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||
import com.github.damontecres.wholphin.ui.main.HomeRow
|
||||
import com.github.damontecres.wholphin.ui.main.HomeSection
|
||||
|
|
@ -126,7 +127,7 @@ class RecommendedTvShowViewModel
|
|||
HomeRow(HomeSection.LATEST_MEDIA, recentlyAddedItems, "Recently Added"),
|
||||
HomeRow(HomeSection.NONE, suggestedItems, "Suggestions"),
|
||||
HomeRow(HomeSection.NONE, unwatchedTopRatedItems, "Top Rated Unwatched"),
|
||||
)
|
||||
).filter { it.items.isNotEmpty() }
|
||||
withContext(Dispatchers.Main) {
|
||||
this@RecommendedTvShowViewModel.rows.value = homeRows
|
||||
loading.value = LoadingState.Success
|
||||
|
|
@ -143,6 +144,7 @@ fun RecommendedTvShow(
|
|||
preferences: UserPreferences,
|
||||
parentId: UUID,
|
||||
onClickItem: (BaseItem) -> Unit,
|
||||
onFocusPosition: (RowColumn) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: RecommendedTvShowViewModel = hiltViewModel(),
|
||||
) {
|
||||
|
|
@ -163,6 +165,7 @@ fun RecommendedTvShow(
|
|||
HomePageContent(
|
||||
homeRows = rows,
|
||||
onClickItem = onClickItem,
|
||||
onFocusPosition = onFocusPosition,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,6 +145,9 @@ fun CollectionFolderMovie(
|
|||
preferences = preferences,
|
||||
onClickItem = onClickItem,
|
||||
parentId = destination.itemId,
|
||||
onFocusPosition = { pos ->
|
||||
showHeader = pos.row < 1
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 16.dp)
|
||||
|
|
|
|||
|
|
@ -145,6 +145,9 @@ fun CollectionFolderTv(
|
|||
preferences = preferences,
|
||||
parentId = destination.itemId,
|
||||
onClickItem = onClickItem,
|
||||
onFocusPosition = { pos ->
|
||||
showHeader = pos.row < 1
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 16.dp)
|
||||
|
|
|
|||
|
|
@ -75,11 +75,7 @@ abstract class LoadingItemViewModel(
|
|||
) + Dispatchers.IO,
|
||||
) {
|
||||
try {
|
||||
val fetchedItem = api.userLibraryApi.getItem(itemId).content
|
||||
withContext(Dispatchers.Main) {
|
||||
item.value = BaseItem.from(fetchedItem, api)
|
||||
loading.value = LoadingState.Success
|
||||
}
|
||||
fetchAndSetItem(itemId)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to load item $itemId")
|
||||
withContext(Dispatchers.Main) {
|
||||
|
|
@ -89,4 +85,13 @@ abstract class LoadingItemViewModel(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
open suspend fun fetchAndSetItem(itemId: UUID) =
|
||||
withContext(Dispatchers.IO) {
|
||||
val fetchedItem = api.userLibraryApi.getItem(itemId).content
|
||||
withContext(Dispatchers.Main) {
|
||||
item.value = BaseItem.from(fetchedItem, api)
|
||||
loading.value = LoadingState.Success
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -28,10 +29,10 @@ import androidx.compose.ui.draw.alpha
|
|||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
|
|
@ -68,7 +69,9 @@ import com.github.damontecres.wholphin.ui.nav.Destination
|
|||
import com.github.damontecres.wholphin.ui.rememberPosition
|
||||
import com.github.damontecres.wholphin.ui.roundMinutes
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jellyfin.sdk.model.extensions.ticks
|
||||
import kotlin.time.Duration
|
||||
|
||||
|
|
@ -237,6 +240,7 @@ fun SeriesDetailsContent(
|
|||
overviewOnClick = overviewOnClick,
|
||||
playOnClick = playOnClick,
|
||||
watchOnClick = watchOnClick,
|
||||
bringIntoViewRequester = bringIntoViewRequester,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth(.6f)
|
||||
|
|
@ -301,12 +305,13 @@ fun SeriesDetailsContent(
|
|||
fun SeriesDetailsHeader(
|
||||
series: BaseItem,
|
||||
played: Boolean,
|
||||
bringIntoViewRequester: BringIntoViewRequester,
|
||||
overviewOnClick: () -> Unit,
|
||||
playOnClick: () -> Unit,
|
||||
watchOnClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val dto = series.data
|
||||
val details =
|
||||
buildList {
|
||||
|
|
@ -370,13 +375,27 @@ fun SeriesDetailsHeader(
|
|||
resume = Duration.ZERO,
|
||||
icon = Icons.Default.PlayArrow,
|
||||
onClick = { playOnClick.invoke() },
|
||||
modifier = Modifier,
|
||||
modifier =
|
||||
Modifier.onFocusChanged {
|
||||
if (it.isFocused) {
|
||||
scope.launch(ExceptionHandler()) {
|
||||
bringIntoViewRequester.bringIntoView()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
ExpandableFaButton(
|
||||
title = if (played) R.string.mark_unwatched else R.string.mark_watched,
|
||||
iconStringRes = if (played) R.string.fa_eye else R.string.fa_eye_slash,
|
||||
onClick = watchOnClick,
|
||||
modifier = Modifier,
|
||||
modifier =
|
||||
Modifier.onFocusChanged {
|
||||
if (it.isFocused) {
|
||||
scope.launch(ExceptionHandler()) {
|
||||
bringIntoViewRequester.bringIntoView()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ class SeriesViewModel
|
|||
} else {
|
||||
api.playStateApi.markUnplayedItem(seriesId)
|
||||
}
|
||||
init(prefs, seriesId, null, null, null)
|
||||
fetchItem(seriesId, null)
|
||||
}
|
||||
|
||||
fun refreshEpisode(
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class MovieViewModel
|
|||
} else {
|
||||
api.playStateApi.markUnplayedItem(itemId)
|
||||
}
|
||||
init(itemId, null)
|
||||
fetchAndSetItem(itemId)
|
||||
}
|
||||
|
||||
fun savePlayVersion(
|
||||
|
|
@ -377,7 +377,7 @@ fun MovieDetailsContent(
|
|||
bringIntoViewRequester = bringIntoViewRequester,
|
||||
overviewOnClick = overviewOnClick,
|
||||
Modifier
|
||||
.fillMaxWidth(.7f)
|
||||
.fillMaxWidth(.75f)
|
||||
.padding(bottom = 8.dp),
|
||||
)
|
||||
ExpandablePlayButtons(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.github.damontecres.wholphin.ui.detail.movie
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -10,6 +12,8 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
|
|
@ -63,7 +67,7 @@ fun MovieDetailsHeader(
|
|||
text = movie.name ?: "",
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style =
|
||||
MaterialTheme.typography.displayLarge.copy(
|
||||
MaterialTheme.typography.displayMedium.copy(
|
||||
shadow =
|
||||
Shadow(
|
||||
color = Color.DarkGray,
|
||||
|
|
@ -116,13 +120,26 @@ fun MovieDetailsHeader(
|
|||
}
|
||||
}
|
||||
|
||||
dto.taglines?.firstOrNull()?.let { tagline ->
|
||||
Text(
|
||||
text = tagline,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
}
|
||||
|
||||
// Description
|
||||
dto.overview?.let { overview ->
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val focused = interactionSource.collectIsFocusedAsState().value
|
||||
LaunchedEffect(focused) {
|
||||
if (focused) bringIntoViewRequester.bringIntoView()
|
||||
}
|
||||
OverviewText(
|
||||
overview = overview,
|
||||
maxLines = 3,
|
||||
onClick = overviewOnClick,
|
||||
textBoxHeight = Dp.Unspecified,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
}
|
||||
movie.data.people
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import androidx.compose.ui.draw.alpha
|
|||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
|
|
@ -99,6 +100,7 @@ fun HomePageContent(
|
|||
homeRows: List<HomeRow>,
|
||||
onClickItem: (BaseItem) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onFocusPosition: ((RowColumn) -> Unit)? = null,
|
||||
) {
|
||||
var position by rememberSaveable(stateSaver = RowColumnSaver) {
|
||||
mutableStateOf(RowColumn(0, 0))
|
||||
|
|
@ -198,7 +200,16 @@ fun HomePageContent(
|
|||
).ifElse(
|
||||
RowColumn(rowIndex, index) == position,
|
||||
Modifier.focusRequester(positionFocusRequester),
|
||||
).onFocusChanged {
|
||||
if (it.isFocused) {
|
||||
onFocusPosition?.invoke(
|
||||
RowColumn(
|
||||
rowIndex,
|
||||
index,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
interactionSource = null,
|
||||
cardHeight = Cards.height2x3,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue