mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Basic save/load locally
This commit is contained in:
parent
84dc56289c
commit
ee8a4ac0ac
8 changed files with 392 additions and 123 deletions
|
|
@ -99,7 +99,7 @@ sealed class HomeRowConfig {
|
||||||
val parentId: UUID,
|
val parentId: UUID,
|
||||||
override val viewOptions: HomeRowViewOptions =
|
override val viewOptions: HomeRowViewOptions =
|
||||||
HomeRowViewOptions(
|
HomeRowViewOptions(
|
||||||
heightDp = (Cards.HEIGHT_2X3_DP * .75f).toInt(),
|
heightDp = (Cards.HEIGHT_2X3_DP * .75f).toInt().let { it - it % 4 },
|
||||||
aspectRatio = AspectRatio.WIDE,
|
aspectRatio = AspectRatio.WIDE,
|
||||||
),
|
),
|
||||||
) : HomeRowConfig() {
|
) : HomeRowConfig() {
|
||||||
|
|
@ -128,7 +128,7 @@ sealed class HomeRowConfig {
|
||||||
@SerialName("GetItems")
|
@SerialName("GetItems")
|
||||||
data class GetItems(
|
data class GetItems(
|
||||||
override val id: Int,
|
override val id: Int,
|
||||||
val name: String?,
|
val name: String,
|
||||||
val getItems: GetItemsRequest,
|
val getItems: GetItemsRequest,
|
||||||
override val viewOptions: HomeRowViewOptions,
|
override val viewOptions: HomeRowViewOptions,
|
||||||
) : HomeRowConfig() {
|
) : HomeRowConfig() {
|
||||||
|
|
@ -142,8 +142,8 @@ data class HomeRowConfigDisplay(
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("HomePageConfiguration")
|
@SerialName("HomePageSettings")
|
||||||
data class HomePageConfiguration(
|
data class HomePageSettings(
|
||||||
val version: Int = 1,
|
val version: Int = 1,
|
||||||
val rows: List<HomeRowConfig>,
|
val rows: List<HomeRowConfig>,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
package com.github.damontecres.wholphin.services
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.damontecres.wholphin.R
|
||||||
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
|
import com.github.damontecres.wholphin.data.model.HomePageSettings
|
||||||
|
import com.github.damontecres.wholphin.ui.toServerString
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
|
import kotlinx.serialization.json.encodeToStream
|
||||||
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
|
import org.jellyfin.sdk.api.client.extensions.displayPreferencesApi
|
||||||
|
import org.jellyfin.sdk.model.UUID
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class HomeSettingsService
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
@param:ApplicationContext private val context: Context,
|
||||||
|
private val api: ApiClient,
|
||||||
|
private val serverRepository: ServerRepository,
|
||||||
|
) {
|
||||||
|
val jsonParser =
|
||||||
|
Json {
|
||||||
|
isLenient = true
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveToServer(
|
||||||
|
settings: HomePageSettings,
|
||||||
|
displayPreferencesId: String = DISPLAY_PREF_ID,
|
||||||
|
) {
|
||||||
|
serverRepository.currentUser.value?.let { user ->
|
||||||
|
val current = getDisplayPreferences(user.id, DISPLAY_PREF_ID)
|
||||||
|
val customPrefs =
|
||||||
|
current.customPrefs.toMutableMap().apply {
|
||||||
|
put(CUSTOM_PREF_ID, jsonParser.encodeToString(settings))
|
||||||
|
}
|
||||||
|
api.displayPreferencesApi.updateDisplayPreferences(
|
||||||
|
displayPreferencesId = displayPreferencesId,
|
||||||
|
userId = user.id,
|
||||||
|
client = context.getString(R.string.app_name),
|
||||||
|
data = current.copy(customPrefs = customPrefs),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun loadFromServer(displayPreferencesId: String = DISPLAY_PREF_ID): HomePageSettings? =
|
||||||
|
serverRepository.currentUser.value?.let { user ->
|
||||||
|
val current = getDisplayPreferences(user.id, displayPreferencesId)
|
||||||
|
current.customPrefs[DISPLAY_PREF_ID]?.let {
|
||||||
|
Json.decodeFromString<HomePageSettings>(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getDisplayPreferences(
|
||||||
|
userId: UUID,
|
||||||
|
displayPreferencesId: String,
|
||||||
|
) = api.displayPreferencesApi
|
||||||
|
.getDisplayPreferences(
|
||||||
|
userId = userId,
|
||||||
|
displayPreferencesId = displayPreferencesId,
|
||||||
|
client = context.getString(R.string.app_name),
|
||||||
|
).content
|
||||||
|
|
||||||
|
private fun filename(userId: UUID) = "${CUSTOM_PREF_ID}_${userId.toServerString()}.json"
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
suspend fun saveToLocal(settings: HomePageSettings) {
|
||||||
|
serverRepository.currentUser.value?.let { user ->
|
||||||
|
val dir = File(context.filesDir, CUSTOM_PREF_ID)
|
||||||
|
dir.mkdirs()
|
||||||
|
File(dir, filename(user.id)).outputStream().use {
|
||||||
|
jsonParser.encodeToStream(settings, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
suspend fun loadFromLocal(): HomePageSettings? =
|
||||||
|
serverRepository.currentUser.value?.let { user ->
|
||||||
|
val dir = File(context.filesDir, CUSTOM_PREF_ID)
|
||||||
|
val file = File(dir, filename(user.id))
|
||||||
|
if (file.exists()) {
|
||||||
|
file.inputStream().use {
|
||||||
|
jsonParser.decodeFromStream<HomePageSettings>(it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DISPLAY_PREF_ID = "default"
|
||||||
|
const val CUSTOM_PREF_ID = "home_settings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -195,6 +195,7 @@ fun HomePageContent(
|
||||||
onFocusPosition: ((RowColumn) -> Unit)? = null,
|
onFocusPosition: ((RowColumn) -> Unit)? = null,
|
||||||
loadingState: LoadingState? = null,
|
loadingState: LoadingState? = null,
|
||||||
listState: LazyListState = rememberLazyListState(),
|
listState: LazyListState = rememberLazyListState(),
|
||||||
|
takeFocus: Boolean = true,
|
||||||
) {
|
) {
|
||||||
var position by rememberPosition()
|
var position by rememberPosition()
|
||||||
val focusedItem =
|
val focusedItem =
|
||||||
|
|
@ -204,6 +205,7 @@ fun HomePageContent(
|
||||||
|
|
||||||
val rowFocusRequesters = remember(homeRows) { List(homeRows.size) { FocusRequester() } }
|
val rowFocusRequesters = remember(homeRows) { List(homeRows.size) { FocusRequester() } }
|
||||||
var firstFocused by remember { mutableStateOf(false) }
|
var firstFocused by remember { mutableStateOf(false) }
|
||||||
|
if (takeFocus) {
|
||||||
LaunchedEffect(homeRows) {
|
LaunchedEffect(homeRows) {
|
||||||
if (!firstFocused) {
|
if (!firstFocused) {
|
||||||
if (position.row >= 0) {
|
if (position.row >= 0) {
|
||||||
|
|
@ -223,6 +225,7 @@ fun HomePageContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
LaunchedEffect(position) {
|
LaunchedEffect(position) {
|
||||||
if (position.row >= 0) {
|
if (position.row >= 0) {
|
||||||
listState.animateScrollToItem(position.row)
|
listState.animateScrollToItem(position.row)
|
||||||
|
|
@ -421,19 +424,17 @@ fun HomePageHeader(
|
||||||
item: BaseItem?,
|
item: BaseItem?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
item?.let {
|
val isEpisode = item?.type == BaseItemKind.EPISODE
|
||||||
val isEpisode = item.type == BaseItemKind.EPISODE
|
val dto = item?.data
|
||||||
val dto = item.data
|
|
||||||
HomePageHeader(
|
HomePageHeader(
|
||||||
title = item.title,
|
title = item?.title,
|
||||||
subtitle = if (isEpisode) dto.name else null,
|
subtitle = if (isEpisode) dto?.name else null,
|
||||||
overview = dto.overview,
|
overview = dto?.overview,
|
||||||
overviewTwoLines = isEpisode,
|
overviewTwoLines = isEpisode,
|
||||||
quickDetails = item.ui.quickDetails,
|
quickDetails = item?.ui?.quickDetails ?: AnnotatedString(""),
|
||||||
timeRemaining = item.timeRemainingOrRuntime,
|
timeRemaining = item?.timeRemainingOrRuntime,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,22 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||||
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
import com.github.damontecres.wholphin.ui.preferences.ComposablePreference
|
import com.github.damontecres.wholphin.ui.preferences.ComposablePreference
|
||||||
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeRowSettings(
|
fun HomeRowSettings(
|
||||||
|
|
@ -25,6 +30,8 @@ fun HomeRowSettings(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
defaultViewOptions: HomeRowViewOptions = HomeRowViewOptions(),
|
defaultViewOptions: HomeRowViewOptions = HomeRowViewOptions(),
|
||||||
) {
|
) {
|
||||||
|
val firstFocus = remember { FocusRequester() }
|
||||||
|
LaunchedEffect(Unit) { firstFocus.tryRequestFocus() }
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
|
|
@ -32,7 +39,7 @@ fun HomeRowSettings(
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
items(HomeRowViewOptions.OPTIONS) { pref ->
|
itemsIndexed(HomeRowViewOptions.OPTIONS) { index, pref ->
|
||||||
pref as AppPreference<HomeRowViewOptions, Any>
|
pref as AppPreference<HomeRowViewOptions, Any>
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val value = pref.getter.invoke(viewOptions)
|
val value = pref.getter.invoke(viewOptions)
|
||||||
|
|
@ -51,7 +58,13 @@ fun HomeRowSettings(
|
||||||
onApplyApplyAll.invoke()
|
onApplyApplyAll.invoke()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.background(MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp)),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.surfaceColorAtElevation(
|
||||||
|
5.dp,
|
||||||
|
),
|
||||||
|
).ifElse(index == 0, Modifier.focusRequester(firstFocus)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.github.damontecres.wholphin.ui.main.settings
|
package com.github.damontecres.wholphin.ui.main.settings
|
||||||
|
|
||||||
|
import androidx.navigation3.runtime.NavKey
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracking the pages for selecting and configuring rows
|
* Tracking the pages for selecting and configuring rows
|
||||||
*/
|
*/
|
||||||
sealed interface HomeSettingsDestination {
|
sealed interface HomeSettingsDestination : NavKey {
|
||||||
data object RowList : HomeSettingsDestination
|
data object RowList : HomeSettingsDestination
|
||||||
|
|
||||||
data object ChooseLibrary : HomeSettingsDestination
|
data object ChooseLibrary : HomeSettingsDestination
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,16 @@ import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||||
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
val settingsWidth = 300.dp
|
val settingsWidth = 300.dp
|
||||||
|
|
||||||
|
|
@ -29,10 +33,12 @@ fun HomeSettingsPage(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
viewModel: HomeSettingsViewModel = hiltViewModel(),
|
viewModel: HomeSettingsViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val state by viewModel.state.collectAsState()
|
val scope = rememberCoroutineScope()
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
var destination by remember { mutableStateOf<HomeSettingsDestination>(HomeSettingsDestination.RowList) }
|
var destination by remember { mutableStateOf<HomeSettingsDestination>(HomeSettingsDestination.RowList) }
|
||||||
|
|
||||||
|
val state by viewModel.state.collectAsState()
|
||||||
|
|
||||||
BackHandler(destination is HomeSettingsDestination.ChooseRowType) {
|
BackHandler(destination is HomeSettingsDestination.ChooseRowType) {
|
||||||
destination = HomeSettingsDestination.ChooseLibrary
|
destination = HomeSettingsDestination.ChooseLibrary
|
||||||
}
|
}
|
||||||
|
|
@ -62,10 +68,15 @@ fun HomeSettingsPage(
|
||||||
HomeSettingsRowList(
|
HomeSettingsRowList(
|
||||||
state = state,
|
state = state,
|
||||||
onClickAdd = { destination = HomeSettingsDestination.ChooseLibrary },
|
onClickAdd = { destination = HomeSettingsDestination.ChooseLibrary },
|
||||||
|
onClickSaveLocal = { viewModel.saveToLocal() },
|
||||||
onClickMove = viewModel::moveRow,
|
onClickMove = viewModel::moveRow,
|
||||||
onClickDelete = viewModel::deleteRow,
|
onClickDelete = viewModel::deleteRow,
|
||||||
onClick = { index, row ->
|
onClick = { index, row ->
|
||||||
destination = HomeSettingsDestination.RowSettings(row.config.id)
|
destination = HomeSettingsDestination.RowSettings(row.config.id)
|
||||||
|
scope.launch(ExceptionHandler()) {
|
||||||
|
Timber.v("Scroll to $index")
|
||||||
|
listState.scrollToItem(index)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = destModifier,
|
modifier = destModifier,
|
||||||
)
|
)
|
||||||
|
|
@ -121,6 +132,7 @@ fun HomeSettingsPage(
|
||||||
showClock = false,
|
showClock = false,
|
||||||
onUpdateBackdrop = viewModel::updateBackdrop,
|
onUpdateBackdrop = viewModel::updateBackdrop,
|
||||||
listState = listState,
|
listState = listState,
|
||||||
|
takeFocus = false,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,12 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Create
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -33,7 +37,7 @@ import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
||||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||||
import com.github.damontecres.wholphin.ui.components.Button
|
import com.github.damontecres.wholphin.ui.components.Button
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
|
||||||
enum class MoveDirection {
|
enum class MoveDirection {
|
||||||
UP,
|
UP,
|
||||||
|
|
@ -44,12 +48,14 @@ enum class MoveDirection {
|
||||||
fun HomeSettingsRowList(
|
fun HomeSettingsRowList(
|
||||||
state: HomePageSettingsState,
|
state: HomePageSettingsState,
|
||||||
onClick: (Int, HomeRowConfigDisplay) -> Unit,
|
onClick: (Int, HomeRowConfigDisplay) -> Unit,
|
||||||
|
onClickSaveLocal: () -> Unit,
|
||||||
onClickAdd: () -> Unit,
|
onClickAdd: () -> Unit,
|
||||||
onClickMove: (MoveDirection, Int) -> Unit,
|
onClickMove: (MoveDirection, Int) -> Unit,
|
||||||
onClickDelete: (Int) -> Unit,
|
onClickDelete: (Int) -> Unit,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
firstFocus: FocusRequester = remember { FocusRequester() },
|
firstFocus: FocusRequester = remember { FocusRequester() },
|
||||||
) {
|
) {
|
||||||
|
LaunchedEffect(Unit) { firstFocus.tryRequestFocus() }
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
|
@ -58,6 +64,45 @@ fun HomeSettingsRowList(
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.focusRestorer(firstFocus),
|
.focusRestorer(firstFocus),
|
||||||
) {
|
) {
|
||||||
|
item {
|
||||||
|
ListItem(
|
||||||
|
selected = false,
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.add_row),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Add,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = onClickAdd,
|
||||||
|
modifier = Modifier.focusRequester(firstFocus),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
ListItem(
|
||||||
|
selected = false,
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.save),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Create,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = onClickSaveLocal,
|
||||||
|
modifier = Modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
HorizontalDivider()
|
||||||
|
}
|
||||||
itemsIndexed(state.rows, key = { _, row -> row.config.id }) { index, row ->
|
itemsIndexed(state.rows, key = { _, row -> row.config.id }) { index, row ->
|
||||||
HomeRowConfigContent(
|
HomeRowConfigContent(
|
||||||
config = row,
|
config = row,
|
||||||
|
|
@ -69,20 +114,9 @@ fun HomeSettingsRowList(
|
||||||
onClick = { onClick.invoke(index, row) },
|
onClick = { onClick.invoke(index, row) },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.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),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.github.damontecres.wholphin.ui.main.settings
|
package com.github.damontecres.wholphin.ui.main.settings
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
|
@ -8,11 +9,13 @@ import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.NavDrawerItemRepository
|
import com.github.damontecres.wholphin.data.NavDrawerItemRepository
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.wholphin.data.model.HomePageSettings
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||||
import com.github.damontecres.wholphin.preferences.HomePagePreferences
|
import com.github.damontecres.wholphin.preferences.HomePagePreferences
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
|
import com.github.damontecres.wholphin.services.HomeSettingsService
|
||||||
import com.github.damontecres.wholphin.services.ImageUrlService
|
import com.github.damontecres.wholphin.services.ImageUrlService
|
||||||
import com.github.damontecres.wholphin.services.LatestNextUpService
|
import com.github.damontecres.wholphin.services.LatestNextUpService
|
||||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||||
|
|
@ -21,6 +24,7 @@ import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.components.getGenreImageMap
|
import com.github.damontecres.wholphin.ui.components.getGenreImageMap
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
|
import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem
|
||||||
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.util.GetGenresRequestHandler
|
import com.github.damontecres.wholphin.util.GetGenresRequestHandler
|
||||||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||||
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
||||||
|
|
@ -40,6 +44,7 @@ import org.jellyfin.sdk.model.api.request.GetGenresRequest
|
||||||
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||||
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
||||||
import org.jellyfin.sdk.model.serializer.toUUID
|
import org.jellyfin.sdk.model.serializer.toUUID
|
||||||
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -49,6 +54,7 @@ class HomeSettingsViewModel
|
||||||
constructor(
|
constructor(
|
||||||
@param:ApplicationContext private val context: Context,
|
@param:ApplicationContext private val context: Context,
|
||||||
private val api: ApiClient,
|
private val api: ApiClient,
|
||||||
|
private val homeSettingsService: HomeSettingsService,
|
||||||
private val serverRepository: ServerRepository,
|
private val serverRepository: ServerRepository,
|
||||||
private val userPreferencesService: UserPreferencesService,
|
private val userPreferencesService: UserPreferencesService,
|
||||||
private val navDrawerItemRepository: NavDrawerItemRepository,
|
private val navDrawerItemRepository: NavDrawerItemRepository,
|
||||||
|
|
@ -73,10 +79,24 @@ class HomeSettingsViewModel
|
||||||
}
|
}
|
||||||
_state.update { it.copy(libraries = libraries) }
|
_state.update { it.copy(libraries = libraries) }
|
||||||
|
|
||||||
// TODO get config
|
val localSettings =
|
||||||
|
try {
|
||||||
|
homeSettingsService.loadFromLocal()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex)
|
||||||
|
showToast(context, "Error loading settings: ${ex.localizedMessage}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (localSettings != null) {
|
||||||
|
val displays = localSettings.rows.map { convert(it) }
|
||||||
|
_state.update {
|
||||||
|
it.copy(rows = displays)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
// Or create default
|
// Or create default
|
||||||
val prefs = userPreferencesService.getCurrent().appPreferences.homePagePreferences
|
val prefs =
|
||||||
|
userPreferencesService.getCurrent().appPreferences.homePagePreferences
|
||||||
val includedIds =
|
val includedIds =
|
||||||
navDrawerItemRepository
|
navDrawerItemRepository
|
||||||
.getFilteredNavDrawerItems(navDrawerItems)
|
.getFilteredNavDrawerItems(navDrawerItems)
|
||||||
|
|
@ -151,13 +171,84 @@ class HomeSettingsViewModel
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(rows = rowConfig)
|
it.copy(rows = rowConfig)
|
||||||
}
|
}
|
||||||
// val json = Json.encodeToString(rowConfig)
|
}
|
||||||
// Timber.v("json=$json")
|
|
||||||
|
|
||||||
fetchRowData()
|
fetchRowData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun convert(config: HomeRowConfig): HomeRowConfigDisplay =
|
||||||
|
when (config) {
|
||||||
|
is HomeRowConfig.ByParent -> {
|
||||||
|
val name =
|
||||||
|
api.userLibraryApi
|
||||||
|
.getItem(itemId = config.parentId)
|
||||||
|
.content.name ?: ""
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
name,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.ContinueWatching -> {
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
context.getString(R.string.continue_watching),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.ContinueWatchingCombined -> {
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
context.getString(R.string.combine_continue_next),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.Genres -> {
|
||||||
|
val name =
|
||||||
|
api.userLibraryApi
|
||||||
|
.getItem(itemId = config.parentId)
|
||||||
|
.content.name ?: ""
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
context.getString(R.string.genres_in, name),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.GetItems -> {
|
||||||
|
HomeRowConfigDisplay(config.name, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.NextUp -> {
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
context.getString(R.string.next_up),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.RecentlyAdded -> {
|
||||||
|
val name =
|
||||||
|
api.userLibraryApi
|
||||||
|
.getItem(itemId = config.parentId)
|
||||||
|
.content.name ?: ""
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
context.getString(R.string.recently_added_in, name),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is HomeRowConfig.RecentlyReleased -> {
|
||||||
|
val name =
|
||||||
|
api.userLibraryApi
|
||||||
|
.getItem(itemId = config.parentId)
|
||||||
|
.content.name ?: ""
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
context.getString(R.string.recently_released_in, name),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun updateBackdrop(item: BaseItem) {
|
fun updateBackdrop(item: BaseItem) {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
backdropService.submit(item)
|
backdropService.submit(item)
|
||||||
|
|
@ -615,6 +706,20 @@ class HomeSettingsViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveToLocal() {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
val rows = state.value.rows.map { it.config }
|
||||||
|
val settings = HomePageSettings(rows = rows)
|
||||||
|
try {
|
||||||
|
homeSettingsService.saveToLocal(settings)
|
||||||
|
showToast(context, context.getString(R.string.save), Toast.LENGTH_SHORT)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex)
|
||||||
|
showToast(context, "Error saving: ${ex.localizedMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HomePageSettingsState(
|
data class HomePageSettingsState(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue