mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
feat: add initial focus management and media metadata for TV home screen
Introduce a `FocusableItem` interface to unify media item properties (ID, type, text, and images) across `ContinueWatchingItem`, `NextUpItem`, and `PosterItem`. Key changes: - Implement automatic initial focus on the first available item in `TvHomeContent`. - Add `onMediaFocused` callbacks to TV sections and cards to track active items. - Standardize image URL generation and metadata access in home models. - Clean up unused previews in `HomeScreen`. - Add UI tests for TV home content focus behavior.
This commit is contained in:
@@ -39,6 +39,13 @@ class AppViewModel @Inject constructor(
|
||||
private val _isRefreshing = MutableStateFlow(false)
|
||||
val isRefreshing: StateFlow<Boolean> = _isRefreshing.asStateFlow()
|
||||
|
||||
val serverUrl: StateFlow<String> = userSessionRepository.serverUrl
|
||||
.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000),
|
||||
""
|
||||
)
|
||||
|
||||
val libraries = appContentRepository.libraries.map { libraries ->
|
||||
libraries.map {
|
||||
LibraryItem(
|
||||
|
||||
@@ -88,26 +88,51 @@ data class SuggestedMovie (
|
||||
)
|
||||
) : SuggestedItem
|
||||
|
||||
sealed interface FocusableItem {
|
||||
val imageUrl: String
|
||||
val primaryText: String
|
||||
val secondaryText: String
|
||||
val description: String
|
||||
val id: UUID
|
||||
val type: BaseItemKind
|
||||
}
|
||||
|
||||
data class ContinueWatchingItem(
|
||||
val type: BaseItemKind,
|
||||
override val type: BaseItemKind,
|
||||
val movie: Movie? = null,
|
||||
val episode: Episode? = null
|
||||
) {
|
||||
val id: UUID = when (type) {
|
||||
val episode: Episode? = null,
|
||||
) : FocusableItem {
|
||||
override val id: UUID = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.id
|
||||
BaseItemKind.EPISODE -> episode!!.id
|
||||
else -> throw UnsupportedOperationException("Unsupported item type: $type")
|
||||
}
|
||||
val primaryText: String = when (type) {
|
||||
override val primaryText: String = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.title
|
||||
BaseItemKind.EPISODE -> episode!!.title
|
||||
else -> throw UnsupportedOperationException("Unsupported item type: $type")
|
||||
}
|
||||
val secondaryText: String = when (type) {
|
||||
override val secondaryText: String = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.year
|
||||
BaseItemKind.EPISODE -> episode!!.releaseDate
|
||||
else -> throw UnsupportedOperationException("Unsupported item type: $type")
|
||||
}
|
||||
override val imageUrl: String = when (type) {
|
||||
BaseItemKind.MOVIE -> JellyfinImageHelper.finishImageUrl(
|
||||
prefixImageUrl = movie!!.imageUrlPrefix,
|
||||
imageType = ImageType.PRIMARY
|
||||
)
|
||||
BaseItemKind.EPISODE -> JellyfinImageHelper.finishImageUrl(
|
||||
prefixImageUrl = episode!!.imageUrlPrefix,
|
||||
imageType = ImageType.PRIMARY
|
||||
)
|
||||
else -> throw IllegalArgumentException("Invalid type: $type")
|
||||
}
|
||||
override val description: String = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.synopsis
|
||||
BaseItemKind.EPISODE -> episode!!.synopsis
|
||||
else -> throw UnsupportedOperationException("Unsupported item type: $type")
|
||||
}
|
||||
val progress: Double = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.progress ?: 0.0
|
||||
BaseItemKind.EPISODE -> episode!!.progress ?: 0.0
|
||||
@@ -117,10 +142,19 @@ data class ContinueWatchingItem(
|
||||
|
||||
data class NextUpItem(
|
||||
val episode: Episode
|
||||
) {
|
||||
val id: UUID = episode.id
|
||||
val primaryText: String = episode.title
|
||||
val secondaryText: String = episode.releaseDate
|
||||
) : FocusableItem {
|
||||
override val id: UUID = episode.id
|
||||
override val type: BaseItemKind
|
||||
get() = BaseItemKind.EPISODE
|
||||
override val imageUrl: String
|
||||
get() = JellyfinImageHelper.finishImageUrl(
|
||||
prefixImageUrl = episode.imageUrlPrefix,
|
||||
imageType = ImageType.PRIMARY
|
||||
)
|
||||
override val primaryText: String = episode.title
|
||||
override val secondaryText: String = episode.releaseDate
|
||||
override val description: String
|
||||
get() = episode.synopsis
|
||||
}
|
||||
|
||||
data class LibraryItem(
|
||||
@@ -132,12 +166,12 @@ data class LibraryItem(
|
||||
)
|
||||
|
||||
data class PosterItem(
|
||||
val type: BaseItemKind,
|
||||
override val type: BaseItemKind,
|
||||
val movie: Movie? = null,
|
||||
val series: Series? = null,
|
||||
val episode: Episode? = null
|
||||
) {
|
||||
val id: UUID = when (type) {
|
||||
) : FocusableItem {
|
||||
override val id: UUID = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.id
|
||||
BaseItemKind.EPISODE -> episode!!.id
|
||||
BaseItemKind.SERIES -> series!!.id
|
||||
@@ -149,7 +183,7 @@ data class PosterItem(
|
||||
BaseItemKind.SERIES -> series!!.name
|
||||
else -> throw IllegalArgumentException("Invalid type: $type")
|
||||
}
|
||||
val imageUrl: String = when (type) {
|
||||
override val imageUrl: String = when (type) {
|
||||
BaseItemKind.MOVIE -> JellyfinImageHelper.finishImageUrl(
|
||||
prefixImageUrl = movie!!.imageUrlPrefix,
|
||||
imageType = ImageType.PRIMARY
|
||||
@@ -164,6 +198,28 @@ data class PosterItem(
|
||||
)
|
||||
else -> throw IllegalArgumentException("Invalid type: $type")
|
||||
}
|
||||
override val primaryText: String
|
||||
get() = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.title
|
||||
BaseItemKind.EPISODE -> episode!!.title
|
||||
BaseItemKind.SERIES -> series!!.name
|
||||
else -> throw IllegalArgumentException("Invalid type: $type")
|
||||
}
|
||||
override val secondaryText: String
|
||||
get() = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.year
|
||||
BaseItemKind.EPISODE -> episode!!.releaseDate
|
||||
BaseItemKind.SERIES -> series!!.year
|
||||
else -> throw IllegalArgumentException("Invalid type: $type")
|
||||
}
|
||||
override val description: String
|
||||
get() = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.synopsis
|
||||
BaseItemKind.EPISODE -> episode!!.synopsis
|
||||
BaseItemKind.SERIES -> series!!.synopsis
|
||||
else -> throw IllegalArgumentException("Invalid type: $type")
|
||||
}
|
||||
|
||||
fun watched() = when (type) {
|
||||
BaseItemKind.MOVIE -> movie!!.watched
|
||||
BaseItemKind.EPISODE -> episode!!.watched
|
||||
|
||||
Reference in New Issue
Block a user