mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Add long click to home page (#66)
Add a popup when long clicking items on the home page Also fixes a bug where the latest Recorded TV row would show non-Live TV items if nothing has ever been recorded.
This commit is contained in:
parent
2667090485
commit
143fc03d9b
6 changed files with 212 additions and 20 deletions
|
|
@ -156,6 +156,7 @@ fun RecommendedMovie(
|
|||
HomePageContent(
|
||||
homeRows = rows,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = {},
|
||||
onFocusPosition = onFocusPosition,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ fun RecommendedTvShow(
|
|||
HomePageContent(
|
||||
homeRows = rows,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = {},
|
||||
onFocusPosition = onFocusPosition,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,17 +2,22 @@ package com.github.damontecres.wholphin.ui.detail
|
|||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||
import androidx.compose.material.icons.filled.ArrowForward
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.ui.components.DialogItem
|
||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.util.supportedPlayableTypes
|
||||
import org.jellyfin.sdk.model.UUID
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.MediaStreamType
|
||||
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Build the [DialogItem]s when clicking "More"
|
||||
|
|
@ -131,3 +136,104 @@ fun buildMoreDialogItems(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun buildMoreDialogItemsForHome(
|
||||
item: BaseItem,
|
||||
seriesId: UUID?,
|
||||
playbackPosition: Duration,
|
||||
watched: Boolean,
|
||||
favorite: Boolean,
|
||||
navigateTo: (Destination) -> Unit,
|
||||
onClickWatch: (UUID, Boolean) -> Unit,
|
||||
onClickFavorite: (UUID, Boolean) -> Unit,
|
||||
): List<DialogItem> =
|
||||
buildList {
|
||||
val itemId = item.id
|
||||
add(
|
||||
DialogItem(
|
||||
"Go To",
|
||||
Icons.Default.ArrowForward,
|
||||
) {
|
||||
navigateTo(item.destination())
|
||||
},
|
||||
)
|
||||
if (item.type in supportedPlayableTypes) {
|
||||
if (playbackPosition >= 1.seconds) {
|
||||
add(
|
||||
DialogItem(
|
||||
"Resume",
|
||||
Icons.Default.PlayArrow,
|
||||
iconColor = Color.Green.copy(alpha = .8f),
|
||||
) {
|
||||
navigateTo(
|
||||
Destination.Playback(
|
||||
itemId,
|
||||
playbackPosition.inWholeMilliseconds,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
add(
|
||||
DialogItem(
|
||||
"Restart",
|
||||
Icons.Default.Refresh,
|
||||
// iconColor = Color.Green.copy(alpha = .8f),
|
||||
) {
|
||||
navigateTo(
|
||||
Destination.Playback(
|
||||
itemId,
|
||||
0L,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
add(
|
||||
DialogItem(
|
||||
"Play",
|
||||
Icons.Default.PlayArrow,
|
||||
iconColor = Color.Green.copy(alpha = .8f),
|
||||
) {
|
||||
navigateTo(
|
||||
Destination.Playback(
|
||||
itemId,
|
||||
0L,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
add(
|
||||
DialogItem(
|
||||
text = if (watched) R.string.mark_unwatched else R.string.mark_watched,
|
||||
iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash,
|
||||
) {
|
||||
onClickWatch.invoke(itemId, !watched)
|
||||
},
|
||||
)
|
||||
add(
|
||||
DialogItem(
|
||||
text = if (favorite) R.string.remove_favorite else R.string.add_favorite,
|
||||
iconStringRes = R.string.fa_heart,
|
||||
iconColor = if (favorite) Color.Red else Color.Unspecified,
|
||||
) {
|
||||
onClickFavorite.invoke(itemId, !favorite)
|
||||
},
|
||||
)
|
||||
seriesId?.let {
|
||||
add(
|
||||
DialogItem(
|
||||
"Go to series",
|
||||
Icons.AutoMirrored.Filled.ArrowForward,
|
||||
) {
|
||||
navigateTo(
|
||||
Destination.MediaItem(
|
||||
it,
|
||||
BaseItemKind.SERIES,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,14 @@ import com.github.damontecres.wholphin.ui.Cards
|
|||
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||
import com.github.damontecres.wholphin.ui.components.DialogParams
|
||||
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
||||
import com.github.damontecres.wholphin.ui.components.DotSeparatedRow
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumnSaver
|
||||
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.roundMinutes
|
||||
|
|
@ -60,6 +63,7 @@ import com.github.damontecres.wholphin.util.formatDateTime
|
|||
import com.github.damontecres.wholphin.util.seasonEpisode
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.extensions.ticks
|
||||
import kotlin.time.Duration
|
||||
|
||||
data class HomeRow(
|
||||
val section: HomeSection,
|
||||
|
|
@ -88,14 +92,48 @@ fun HomePage(
|
|||
-> LoadingPage()
|
||||
|
||||
LoadingState.Success -> {
|
||||
var dialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||
HomePageContent(
|
||||
homeRows,
|
||||
onClickItem = {
|
||||
viewModel.navigationManager.navigateTo(it.destination())
|
||||
},
|
||||
onLongClickItem = {
|
||||
val dialogItems =
|
||||
buildMoreDialogItemsForHome(
|
||||
item = it,
|
||||
seriesId = it.data.seriesId,
|
||||
playbackPosition =
|
||||
it.data.userData
|
||||
?.playbackPositionTicks
|
||||
?.ticks
|
||||
?: Duration.ZERO,
|
||||
watched = it.data.userData?.played ?: false,
|
||||
favorite = it.data.userData?.isFavorite ?: false,
|
||||
navigateTo = viewModel.navigationManager::navigateTo,
|
||||
onClickWatch = { itemId, played ->
|
||||
viewModel.setWatched(itemId, played)
|
||||
},
|
||||
onClickFavorite = { itemId, favorite ->
|
||||
viewModel.setFavorite(itemId, favorite)
|
||||
},
|
||||
)
|
||||
dialog =
|
||||
DialogParams(
|
||||
title = it.title ?: "",
|
||||
fromLongClick = true,
|
||||
items = dialogItems,
|
||||
)
|
||||
},
|
||||
loadingState = loading,
|
||||
modifier = modifier,
|
||||
)
|
||||
dialog?.let { params ->
|
||||
DialogPopup(
|
||||
params = params,
|
||||
onDismissRequest = { dialog = null },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,6 +142,7 @@ fun HomePage(
|
|||
fun HomePageContent(
|
||||
homeRows: List<HomeRow>,
|
||||
onClickItem: (BaseItem) -> Unit,
|
||||
onLongClickItem: (BaseItem) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onFocusPosition: ((RowColumn) -> Unit)? = null,
|
||||
loadingState: LoadingState? = null,
|
||||
|
|
@ -183,7 +222,7 @@ fun HomePageContent(
|
|||
position = RowColumn(rowIndex, index)
|
||||
}
|
||||
},
|
||||
onLongClickItem = {},
|
||||
onLongClickItem = onLongClickItem,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
cardContent = { index, item, cardModifier, onClick, onLongClick ->
|
||||
// TODO better aspect ration handling?
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.github.damontecres.wholphin.preferences.UserPreferences
|
|||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
||||
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
import com.github.damontecres.wholphin.util.supportItemKinds
|
||||
|
|
@ -21,6 +22,7 @@ import kotlinx.coroutines.withContext
|
|||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.itemsApi
|
||||
import org.jellyfin.sdk.api.client.extensions.liveTvApi
|
||||
import org.jellyfin.sdk.api.client.extensions.playStateApi
|
||||
import org.jellyfin.sdk.api.client.extensions.tvShowsApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||
|
|
@ -46,8 +48,11 @@ class HomeViewModel
|
|||
val loadingState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||
val homeRows = MutableLiveData<List<HomeRow>>()
|
||||
|
||||
private lateinit var preferences: UserPreferences
|
||||
|
||||
fun init(preferences: UserPreferences): Job {
|
||||
loadingState.value = LoadingState.Loading
|
||||
this.preferences = preferences
|
||||
val prefs = preferences.appPreferences.homePagePreferences
|
||||
val limit = prefs.maxItemsPerRow
|
||||
return viewModelScope.launch(
|
||||
|
|
@ -171,7 +176,7 @@ class HomeViewModel
|
|||
latestMediaIncludes
|
||||
.mapNotNull { viewId -> views.items.firstOrNull { it.id == viewId } }
|
||||
.filter { it.collectionType in supportedLatestCollectionTypes }
|
||||
.map { view ->
|
||||
.mapNotNull { view ->
|
||||
val title =
|
||||
if (view.collectionType == CollectionType.LIVETV) {
|
||||
"Recently Recorded"
|
||||
|
|
@ -189,6 +194,7 @@ class HomeViewModel
|
|||
} else {
|
||||
view.id
|
||||
}
|
||||
viewId?.let {
|
||||
val request =
|
||||
GetLatestMediaRequest(
|
||||
fields = SlimItemFields,
|
||||
|
|
@ -209,8 +215,37 @@ class HomeViewModel
|
|||
title = title,
|
||||
)
|
||||
}
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
fun setWatched(
|
||||
itemId: UUID,
|
||||
played: Boolean,
|
||||
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
if (played) {
|
||||
api.playStateApi.markPlayedItem(itemId)
|
||||
} else {
|
||||
api.playStateApi.markUnplayedItem(itemId)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
init(preferences)
|
||||
}
|
||||
}
|
||||
|
||||
fun setFavorite(
|
||||
itemId: UUID,
|
||||
favorite: Boolean,
|
||||
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
if (favorite) {
|
||||
api.userLibraryApi.markFavoriteItem(itemId)
|
||||
} else {
|
||||
api.userLibraryApi.unmarkFavoriteItem(itemId)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
init(preferences)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val supportedLatestCollectionTypes =
|
||||
|
|
|
|||
|
|
@ -38,3 +38,13 @@ val supportedCollectionTypes =
|
|||
CollectionType.BOXSETS,
|
||||
CollectionType.LIVETV,
|
||||
)
|
||||
|
||||
val supportedPlayableTypes =
|
||||
setOf(
|
||||
BaseItemKind.MOVIE,
|
||||
BaseItemKind.EPISODE,
|
||||
BaseItemKind.VIDEO,
|
||||
BaseItemKind.TV_CHANNEL,
|
||||
BaseItemKind.TV_PROGRAM,
|
||||
BaseItemKind.RECORDING,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue