mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
WIP, basic MVP
This commit is contained in:
parent
fc0de2144d
commit
6d53869180
11 changed files with 627 additions and 3 deletions
|
|
@ -265,7 +265,8 @@ class MainActivity : AppCompatActivity() {
|
|||
server = current.server,
|
||||
startDestination =
|
||||
requestedDestination
|
||||
?: Destination.Home(),
|
||||
// TODO Undo
|
||||
?: Destination.HomeSettings,
|
||||
navigationManager = navigationManager,
|
||||
preferences = preferences,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import kotlinx.coroutines.withContext
|
|||
import kotlinx.serialization.Serializable
|
||||
import org.jellyfin.sdk.Jellyfin
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.displayPreferencesApi
|
||||
import org.jellyfin.sdk.api.client.extensions.systemApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userApi
|
||||
import org.jellyfin.sdk.model.api.AuthenticationResult
|
||||
|
|
@ -102,6 +103,27 @@ class ServerRepository
|
|||
currentUserId = updatedUser.id.toServerString()
|
||||
}.build()
|
||||
}
|
||||
val settings =
|
||||
apiClient.displayPreferencesApi
|
||||
.getDisplayPreferences(
|
||||
displayPreferencesId = "settings",
|
||||
userId = user.id,
|
||||
client = "wholphin",
|
||||
).content
|
||||
val newSettings =
|
||||
settings.copy(
|
||||
customPrefs =
|
||||
settings.customPrefs.toMutableMap().apply {
|
||||
put("test_key", "test_value")
|
||||
},
|
||||
)
|
||||
apiClient.displayPreferencesApi.updateDisplayPreferences(
|
||||
displayPreferencesId = "settings",
|
||||
userId = user.id,
|
||||
client = "wholphin",
|
||||
data = newSettings,
|
||||
)
|
||||
Timber.v("settings=$settings")
|
||||
withContext(Dispatchers.Main) {
|
||||
_current.value = CurrentUser(updatedServer, updatedUser)
|
||||
_currentUserDto.value = userDto
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
@file:UseSerializers(UUIDSerializer::class)
|
||||
|
||||
package com.github.damontecres.wholphin.data.model
|
||||
|
||||
import com.github.damontecres.wholphin.preferences.PrefContentScale
|
||||
import com.github.damontecres.wholphin.ui.AspectRatio
|
||||
import com.github.damontecres.wholphin.ui.Cards
|
||||
import com.github.damontecres.wholphin.ui.components.ViewOptionImageType
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import org.jellyfin.sdk.model.serializer.UUIDSerializer
|
||||
import java.util.UUID
|
||||
|
||||
@Serializable
|
||||
sealed class HomeRowConfig {
|
||||
abstract val viewOptions: HomeRowViewOptions
|
||||
|
||||
@Serializable
|
||||
data class ContinueWatching(
|
||||
val combined: Boolean,
|
||||
override val viewOptions: HomeRowViewOptions,
|
||||
) : HomeRowConfig()
|
||||
|
||||
@Serializable
|
||||
data class RecentlyAdded(
|
||||
val parentId: UUID,
|
||||
override val viewOptions: HomeRowViewOptions,
|
||||
) : HomeRowConfig()
|
||||
|
||||
@Serializable
|
||||
data class RecentlyReleased(
|
||||
val parentId: UUID,
|
||||
override val viewOptions: HomeRowViewOptions,
|
||||
) : HomeRowConfig()
|
||||
|
||||
@Serializable
|
||||
data class Genres(
|
||||
val genreId: UUID,
|
||||
val parentId: UUID,
|
||||
override val viewOptions: HomeRowViewOptions,
|
||||
) : HomeRowConfig()
|
||||
}
|
||||
|
||||
data class HomeRowConfigDisplay(
|
||||
val title: String,
|
||||
val config: HomeRowConfig,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class HomeRowViewOptions(
|
||||
val heightDp: Int = Cards.HEIGHT_2X3_DP,
|
||||
val spacing: Int = 16,
|
||||
val contentScale: PrefContentScale = PrefContentScale.FIT,
|
||||
val aspectRatio: AspectRatio = AspectRatio.TALL,
|
||||
val imageType: ViewOptionImageType = ViewOptionImageType.PRIMARY,
|
||||
val showTitles: Boolean = true,
|
||||
val useSeries: Boolean = true,
|
||||
)
|
||||
|
|
@ -73,7 +73,8 @@ val SlimItemFields =
|
|||
)
|
||||
|
||||
object Cards {
|
||||
val height2x3 = 172.dp
|
||||
const val HEIGHT_2X3_DP = 172
|
||||
val height2x3 = HEIGHT_2X3_DP.dp
|
||||
val heightEpisode = height2x3 * .75f
|
||||
val playedPercentHeight = 6.dp
|
||||
val serverUserCircle = height2x3 * .75f
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -192,6 +193,7 @@ fun HomePageContent(
|
|||
modifier: Modifier = Modifier,
|
||||
onFocusPosition: ((RowColumn) -> Unit)? = null,
|
||||
loadingState: LoadingState? = null,
|
||||
listState: LazyListState = rememberLazyListState(),
|
||||
) {
|
||||
var position by rememberPosition()
|
||||
val focusedItem =
|
||||
|
|
@ -199,7 +201,6 @@ fun HomePageContent(
|
|||
(homeRows.getOrNull(it.row) as? HomeRowLoadingState.Success)?.items?.getOrNull(it.column)
|
||||
}
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
val rowFocusRequesters = remember(homeRows) { List(homeRows.size) { FocusRequester() } }
|
||||
var firstFocused by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(homeRows) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
package com.github.damontecres.wholphin.ui.main.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||
import com.github.damontecres.wholphin.ui.components.Button
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
|
||||
enum class MoveDirection {
|
||||
UP,
|
||||
DOWN,
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HomePageRowList(
|
||||
state: HomePageSettingsState,
|
||||
onClickAdd: () -> Unit,
|
||||
onClickMove: (MoveDirection, Int) -> Unit,
|
||||
onClickDelete: (Int) -> Unit,
|
||||
modifier: Modifier,
|
||||
firstFocus: FocusRequester = remember { FocusRequester() },
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxHeight()
|
||||
.focusRestorer(firstFocus),
|
||||
) {
|
||||
itemsIndexed(state.rows) { index, row ->
|
||||
HomeRowConfigContent(
|
||||
config = row,
|
||||
moveUpAllowed = index > 0,
|
||||
moveDownAllowed = index != state.rows.lastIndex,
|
||||
onClickMove = { onClickMove.invoke(it, index) },
|
||||
onClickDelete = { onClickDelete.invoke(index) },
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.ifElse(index == 0, Modifier.focusRequester(firstFocus)),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Button(
|
||||
onClick = onClickAdd,
|
||||
modifier = Modifier.ifElse(state.rows.isEmpty(), Modifier.focusRequester(firstFocus)),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.add_row),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HomeRowConfigContent(
|
||||
config: HomeRowConfigDisplay,
|
||||
moveUpAllowed: Boolean,
|
||||
moveDownAllowed: Boolean,
|
||||
onClickMove: (MoveDirection) -> Unit,
|
||||
onClickDelete: () -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 40.dp, max = 88.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
).padding(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = config.title,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.wrapContentWidth(),
|
||||
) {
|
||||
Button(
|
||||
onClick = { onClickMove.invoke(MoveDirection.UP) },
|
||||
enabled = moveUpAllowed,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.fa_caret_up),
|
||||
fontFamily = FontAwesome,
|
||||
)
|
||||
}
|
||||
Button(
|
||||
onClick = { onClickMove.invoke(MoveDirection.DOWN) },
|
||||
enabled = moveDownAllowed,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.fa_caret_down),
|
||||
fontFamily = FontAwesome,
|
||||
)
|
||||
}
|
||||
Button(
|
||||
onClick = onClickDelete,
|
||||
enabled = true,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = "delete",
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
package com.github.damontecres.wholphin.ui.main.settings
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.NavDrawerItemRepository
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||
import com.github.damontecres.wholphin.services.BackdropService
|
||||
import com.github.damontecres.wholphin.services.LatestNextUpService
|
||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||
import com.github.damontecres.wholphin.ui.main.LatestData
|
||||
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
|
||||
import com.github.damontecres.wholphin.util.GetGenresRequestHandler
|
||||
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.model.api.CollectionType
|
||||
import org.jellyfin.sdk.model.api.request.GetGenresRequest
|
||||
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class HomePageSettingsViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
@param:ApplicationContext private val context: Context,
|
||||
private val api: ApiClient,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val userPreferencesService: UserPreferencesService,
|
||||
private val navDrawerItemRepository: NavDrawerItemRepository,
|
||||
private val backdropService: BackdropService,
|
||||
private val latestNextUpService: LatestNextUpService,
|
||||
) : ViewModel() {
|
||||
private val _state = MutableStateFlow(HomePageSettingsState.EMPTY)
|
||||
val state: StateFlow<HomePageSettingsState> = _state
|
||||
|
||||
init {
|
||||
viewModelScope.launchIO {
|
||||
val navDrawerItems =
|
||||
navDrawerItemRepository
|
||||
.getNavDrawerItems()
|
||||
val libraries =
|
||||
navDrawerItems
|
||||
.filter { it is ServerNavDrawerItem }
|
||||
.map {
|
||||
it as ServerNavDrawerItem
|
||||
Library(it.itemId, it.name, it.type)
|
||||
}
|
||||
_state.update { it.copy(libraries = libraries) }
|
||||
|
||||
// TODO get config
|
||||
|
||||
// Or create default
|
||||
val prefs = userPreferencesService.getCurrent().appPreferences.homePagePreferences
|
||||
val includedIds =
|
||||
navDrawerItemRepository
|
||||
.getFilteredNavDrawerItems(navDrawerItems)
|
||||
.filter { it is ServerNavDrawerItem }
|
||||
.map {
|
||||
val id = (it as ServerNavDrawerItem).itemId
|
||||
val name = libraries.firstOrNull { it.itemId == id }?.name
|
||||
val title =
|
||||
name?.let { context.getString(R.string.recently_added_in, it) }
|
||||
?: context.getString(R.string.recently_added)
|
||||
HomeRowConfigDisplay(
|
||||
title,
|
||||
HomeRowConfig.RecentlyAdded(id, HomeRowViewOptions()),
|
||||
)
|
||||
}
|
||||
val rowConfig =
|
||||
listOf(
|
||||
HomeRowConfigDisplay(
|
||||
context.getString(R.string.continue_watching),
|
||||
HomeRowConfig.ContinueWatching(
|
||||
prefs.combineContinueNext,
|
||||
HomeRowViewOptions(),
|
||||
),
|
||||
),
|
||||
) + includedIds
|
||||
_state.update {
|
||||
it.copy(rows = rowConfig)
|
||||
}
|
||||
|
||||
fetchRowData()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateBackdrop(item: BaseItem) {
|
||||
viewModelScope.launchIO {
|
||||
backdropService.submit(item)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchRowData() {
|
||||
val limit = 6
|
||||
val rows =
|
||||
serverRepository.currentUserDto.value?.let { userDto ->
|
||||
val prefs = userPreferencesService.getCurrent().appPreferences.homePagePreferences
|
||||
state.value.rows
|
||||
.map { it.config }
|
||||
.map { row ->
|
||||
// TODO parallelize
|
||||
when (row) {
|
||||
is HomeRowConfig.ContinueWatching -> {
|
||||
val resume = latestNextUpService.getResume(userDto.id, limit, true)
|
||||
val nextUp =
|
||||
latestNextUpService.getNextUp(
|
||||
userDto.id,
|
||||
limit,
|
||||
prefs.enableRewatchingNextUp,
|
||||
false,
|
||||
)
|
||||
val watching =
|
||||
buildList {
|
||||
if (row.combined) {
|
||||
val items =
|
||||
latestNextUpService.buildCombined(resume, nextUp)
|
||||
add(
|
||||
HomeRowLoadingState.Success(
|
||||
title = context.getString(R.string.continue_watching),
|
||||
items = items,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
if (resume.isNotEmpty()) {
|
||||
add(
|
||||
HomeRowLoadingState.Success(
|
||||
title = context.getString(R.string.continue_watching),
|
||||
items = resume,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (nextUp.isNotEmpty()) {
|
||||
add(
|
||||
HomeRowLoadingState.Success(
|
||||
title = context.getString(R.string.next_up),
|
||||
items = nextUp,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
watching
|
||||
}
|
||||
|
||||
is HomeRowConfig.Genres -> {
|
||||
val request =
|
||||
GetGenresRequest(
|
||||
parentId = row.parentId,
|
||||
userId = userDto.id,
|
||||
limit = limit,
|
||||
)
|
||||
val items =
|
||||
GetGenresRequestHandler
|
||||
.execute(api, request)
|
||||
.content.items
|
||||
.map { BaseItem(it, false) }
|
||||
val name =
|
||||
_state.value.libraries
|
||||
.firstOrNull { it.itemId == row.parentId }
|
||||
?.name
|
||||
val title =
|
||||
name?.let { context.getString(R.string.genres_in, it) }
|
||||
?: context.getString(R.string.genres)
|
||||
listOf(HomeRowLoadingState.Success(title, items))
|
||||
}
|
||||
|
||||
is HomeRowConfig.RecentlyAdded -> {
|
||||
val name =
|
||||
_state.value.libraries
|
||||
.firstOrNull { it.itemId == row.parentId }
|
||||
?.name
|
||||
val title =
|
||||
name?.let { context.getString(R.string.recently_added_in, it) }
|
||||
?: context.getString(R.string.recently_added)
|
||||
val request =
|
||||
GetLatestMediaRequest(
|
||||
fields = SlimItemFields,
|
||||
imageTypeLimit = 1,
|
||||
parentId = row.parentId,
|
||||
groupItems = true,
|
||||
limit = limit,
|
||||
isPlayed = null, // Server will handle user's preference
|
||||
)
|
||||
latestNextUpService.loadLatest(listOf(LatestData(title, request)))
|
||||
}
|
||||
|
||||
is HomeRowConfig.RecentlyReleased -> {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
rows?.let { rows ->
|
||||
_state.update {
|
||||
it.copy(loading = LoadingState.Success, rowData = rows)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> List<T>.move(
|
||||
direction: MoveDirection,
|
||||
index: Int,
|
||||
): List<T> =
|
||||
toMutableList().apply {
|
||||
if (direction == MoveDirection.DOWN) {
|
||||
val down = this[index]
|
||||
val up = this[index + 1]
|
||||
set(index, up)
|
||||
set(index + 1, down)
|
||||
} else {
|
||||
val up = this[index]
|
||||
val down = this[index - 1]
|
||||
set(index - 1, up)
|
||||
set(index, down)
|
||||
}
|
||||
}
|
||||
|
||||
fun moveRow(
|
||||
direction: MoveDirection,
|
||||
index: Int,
|
||||
) {
|
||||
_state.update {
|
||||
val rows = it.rows.move(direction, index)
|
||||
// TODO would be more efficient to move rowData, but uncombined continue watching is two rows
|
||||
// val rowData = it.rowData.move(direction, index)
|
||||
it.copy(
|
||||
loading = LoadingState.Loading,
|
||||
rows = rows,
|
||||
)
|
||||
}
|
||||
viewModelScope.launchIO { fetchRowData() }
|
||||
}
|
||||
|
||||
fun deleteRow(index: Int) {
|
||||
_state.update {
|
||||
val rows = it.rows.toMutableList().apply { removeAt(index) }
|
||||
val rowData = it.rowData.toMutableList().apply { removeAt(index) }
|
||||
it.copy(
|
||||
rows = rows,
|
||||
rowData = rowData,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class HomePageSettingsState(
|
||||
val loading: LoadingState,
|
||||
val rows: List<HomeRowConfigDisplay>,
|
||||
val rowData: List<HomeRowLoadingState>,
|
||||
val libraries: List<Library>,
|
||||
) {
|
||||
companion object {
|
||||
val EMPTY =
|
||||
HomePageSettingsState(
|
||||
LoadingState.Pending,
|
||||
listOf(),
|
||||
listOf(),
|
||||
listOf(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class Library(
|
||||
val itemId: UUID,
|
||||
val name: String,
|
||||
val collectionType: CollectionType,
|
||||
)
|
||||
|
||||
val settingsWidth = 300.dp
|
||||
|
||||
@Composable
|
||||
fun HomePageSettings(
|
||||
modifier: Modifier,
|
||||
viewModel: HomePageSettingsViewModel = hiltViewModel(),
|
||||
) {
|
||||
val state by viewModel.state.collectAsState()
|
||||
val listState = rememberLazyListState()
|
||||
var destination by remember { mutableStateOf<HomePageSettingsDestination>(HomePageSettingsDestination.RowList) }
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
HomePageContent(
|
||||
loadingState = state.loading,
|
||||
homeRows = state.rowData,
|
||||
onClickItem = { _, _ -> },
|
||||
onLongClickItem = { _, _ -> },
|
||||
onClickPlay = { _, _ -> },
|
||||
showClock = false,
|
||||
onUpdateBackdrop = viewModel::updateBackdrop,
|
||||
listState = listState,
|
||||
modifier = Modifier.fillMaxHeight().weight(1f),
|
||||
)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.width(settingsWidth)
|
||||
.fillMaxHeight()
|
||||
.background(color = MaterialTheme.colorScheme.surface),
|
||||
) {
|
||||
when (destination) {
|
||||
HomePageSettingsDestination.RowList -> {
|
||||
HomePageRowList(
|
||||
state = state,
|
||||
onClickAdd = { destination = HomePageSettingsDestination.ChooseLibrary },
|
||||
onClickMove = viewModel::moveRow,
|
||||
onClickDelete = viewModel::deleteRow,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
|
||||
is HomePageSettingsDestination.ChooseLibrary -> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
is HomePageSettingsDestination.ChooseRow -> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
is HomePageSettingsDestination.RowSettings -> {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.github.damontecres.wholphin.ui.main.settings
|
||||
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
||||
|
||||
sealed interface HomePageSettingsDestination {
|
||||
data object RowList : HomePageSettingsDestination
|
||||
|
||||
data object ChooseLibrary : HomePageSettingsDestination
|
||||
|
||||
data class ChooseRow(
|
||||
val library: Library,
|
||||
) : HomePageSettingsDestination
|
||||
|
||||
data class RowSettings(
|
||||
val row: HomeRowConfigDisplay,
|
||||
) : HomePageSettingsDestination
|
||||
}
|
||||
|
|
@ -33,6 +33,9 @@ sealed class Destination(
|
|||
val id: Long = 0L,
|
||||
) : Destination()
|
||||
|
||||
@Serializable
|
||||
data object HomeSettings : Destination(true)
|
||||
|
||||
@Serializable
|
||||
data class Settings(
|
||||
val screen: PreferenceScreenOption,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.github.damontecres.wholphin.ui.detail.series.SeriesOverview
|
|||
import com.github.damontecres.wholphin.ui.discover.DiscoverPage
|
||||
import com.github.damontecres.wholphin.ui.main.HomePage
|
||||
import com.github.damontecres.wholphin.ui.main.SearchPage
|
||||
import com.github.damontecres.wholphin.ui.main.settings.HomePageSettings
|
||||
import com.github.damontecres.wholphin.ui.playback.PlaybackPage
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesPage
|
||||
import com.github.damontecres.wholphin.ui.setup.InstallUpdatePage
|
||||
|
|
@ -60,6 +61,10 @@ fun DestinationContent(
|
|||
)
|
||||
}
|
||||
|
||||
is Destination.HomeSettings -> {
|
||||
HomePageSettings(modifier)
|
||||
}
|
||||
|
||||
is Destination.PlaybackList,
|
||||
is Destination.Playback,
|
||||
-> {
|
||||
|
|
|
|||
|
|
@ -462,6 +462,8 @@
|
|||
<string name="request_4k">Request in 4K</string>
|
||||
<string name="software_decoding_av1">AV1 software decoding</string>
|
||||
<string name="upgrade_mpv_toast">MPV is now the default player except for HDR.\nYou can change this in settings.</string>
|
||||
<string name="add_row">Add row</string>
|
||||
<string name="genres_in">Genres in %1$s</string>
|
||||
|
||||
<string-array name="theme_song_volume">
|
||||
<item>Disabled</item>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue