mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 16:11:20 +02:00
Basic home page & images
This commit is contained in:
parent
76d78246db
commit
51ad92f539
16 changed files with 421 additions and 42 deletions
|
|
@ -1,39 +0,0 @@
|
|||
package com.github.damontecres.dolphin.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.userApi
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class MainViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
val api: ApiClient,
|
||||
) : ViewModel() {
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
api.userApi.getCurrentUser().content.configuration?.let {
|
||||
it.orderedViews
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun todo() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainPage(
|
||||
preferences: UserPreferences,
|
||||
modifier: Modifier,
|
||||
viewModel: MainViewModel = viewModel(),
|
||||
) {
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.github.damontecres.dolphin.ui.cards
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.github.damontecres.dolphin.data.model.DolphinModel
|
||||
import com.github.damontecres.dolphin.data.model.Video
|
||||
|
||||
@Composable
|
||||
fun DolphinCard(
|
||||
item: DolphinModel?,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (item) {
|
||||
is Video ->
|
||||
VideoCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
null -> TODO()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.github.damontecres.dolphin.ui.cards
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.Card
|
||||
import androidx.tv.material3.Text
|
||||
import coil3.compose.AsyncImage
|
||||
import com.github.damontecres.dolphin.data.model.Video
|
||||
|
||||
@Composable
|
||||
fun VideoCard(
|
||||
item: Video,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
cardWidth: Dp = 150.dp,
|
||||
cardHeight: Dp = 200.dp,
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.size(cardWidth, cardHeight),
|
||||
) {
|
||||
Text(
|
||||
text = item.name ?: "",
|
||||
)
|
||||
AsyncImage(
|
||||
model = item.imageUrl,
|
||||
contentDescription = item.name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.github.damontecres.dolphin.ui.main
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.github.damontecres.dolphin.R
|
||||
|
||||
/**
|
||||
* All possible homesections, "synced" with jellyfin-web.
|
||||
*
|
||||
* https://github.com/jellyfin/jellyfin-web/blob/master/src/components/homesections/homesections.js
|
||||
*/
|
||||
enum class HomeSection(
|
||||
val key: String,
|
||||
@param:StringRes val nameRes: Int,
|
||||
) {
|
||||
LATEST_MEDIA("latestmedia", R.string.home_section_latest_media),
|
||||
LIBRARY_TILES_SMALL("smalllibrarytiles", R.string.home_section_library),
|
||||
LIBRARY_BUTTONS("librarybuttons", R.string.home_section_library_small),
|
||||
RESUME("resume", R.string.home_section_resume),
|
||||
RESUME_AUDIO("resumeaudio", R.string.home_section_resume_audio),
|
||||
RESUME_BOOK("resumebook", R.string.home_section_resume_book),
|
||||
ACTIVE_RECORDINGS("activerecordings", R.string.home_section_active_recordings),
|
||||
NEXT_UP("nextup", R.string.home_section_next_up),
|
||||
LIVE_TV("livetv", R.string.home_section_livetv),
|
||||
NONE("none", R.string.home_section_none),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromKey(key: String): HomeSection = entries.firstOrNull { it.key == key } ?: NONE
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
package com.github.damontecres.dolphin.ui.main
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.dolphin.data.model.DolphinModel
|
||||
import com.github.damontecres.dolphin.data.model.convertModel
|
||||
import com.github.damontecres.dolphin.isNotNullOrBlank
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
import com.github.damontecres.dolphin.ui.cards.DolphinCard
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.displayPreferencesApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
data class HomeRow(
|
||||
val section: HomeSection,
|
||||
val items: List<DolphinModel>,
|
||||
)
|
||||
|
||||
@HiltViewModel
|
||||
class MainViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
val api: ApiClient,
|
||||
) : ViewModel() {
|
||||
val homeRows = MutableLiveData<List<HomeRow>>()
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val user = api.userApi.getCurrentUser().content
|
||||
val displayPrefs =
|
||||
api.displayPreferencesApi
|
||||
.getDisplayPreferences(
|
||||
displayPreferencesId = "usersettings",
|
||||
client = "emby",
|
||||
).content
|
||||
val homeSections =
|
||||
displayPrefs.customPrefs.entries
|
||||
.filter { it.key.startsWith("homesection") && it.value.isNotNullOrBlank() }
|
||||
.sortedBy { it.key }
|
||||
.map { HomeSection.fromKey(it.value ?: "") }
|
||||
.filterNot { it == HomeSection.NONE }
|
||||
|
||||
val homeRows =
|
||||
homeSections
|
||||
.mapNotNull { section ->
|
||||
Timber.v("Loading section: %s", section.name)
|
||||
when (section) {
|
||||
HomeSection.LATEST_MEDIA -> {
|
||||
user.configuration?.orderedViews?.firstOrNull()?.let { viewId ->
|
||||
val request =
|
||||
GetLatestMediaRequest(
|
||||
fields = listOf(),
|
||||
imageTypeLimit = 1,
|
||||
parentId = viewId,
|
||||
groupItems = true,
|
||||
limit = 25,
|
||||
)
|
||||
val latest =
|
||||
api.userLibraryApi
|
||||
.getLatestMedia(request)
|
||||
.content
|
||||
.map { convertModel(it, api) }
|
||||
Timber.v("latest: %s", latest)
|
||||
// api.itemsApi.getItems(request)
|
||||
HomeRow(
|
||||
section = section,
|
||||
items = latest,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
HomeSection.LIBRARY_TILES_SMALL -> null
|
||||
HomeSection.RESUME -> null
|
||||
HomeSection.ACTIVE_RECORDINGS -> null
|
||||
HomeSection.NEXT_UP -> null
|
||||
HomeSection.LIVE_TV -> null
|
||||
|
||||
// TODO Not supported?
|
||||
HomeSection.LIBRARY_BUTTONS -> null
|
||||
HomeSection.RESUME_AUDIO -> null
|
||||
HomeSection.RESUME_BOOK -> null
|
||||
HomeSection.NONE -> null
|
||||
}
|
||||
}.filter { it.items.isNotEmpty() }
|
||||
withContext(Dispatchers.Main) {
|
||||
this@MainViewModel.homeRows.value = homeRows
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainPage(
|
||||
preferences: UserPreferences,
|
||||
modifier: Modifier,
|
||||
viewModel: MainViewModel = viewModel(),
|
||||
) {
|
||||
val homeRows by viewModel.homeRows.observeAsState(listOf())
|
||||
Column(modifier = modifier) {
|
||||
// TODO header?
|
||||
LazyColumn {
|
||||
homeRows.forEach { row ->
|
||||
item {
|
||||
HomePageRow(
|
||||
row = row,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HomePageRow(
|
||||
row: HomeRow,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(row.section.nameRes),
|
||||
)
|
||||
LazyRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
items(row.items) { item ->
|
||||
DolphinCard(
|
||||
item = item,
|
||||
onClick = {},
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue