mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Save/load from server
This commit is contained in:
parent
e11b9ecc72
commit
f315531db7
7 changed files with 192 additions and 33 deletions
|
|
@ -399,7 +399,7 @@ class HomeSettingsService
|
||||||
/**
|
/**
|
||||||
* Converts a [HomeRowConfig] into [HomeRowConfigDisplay] for UI purposes
|
* Converts a [HomeRowConfig] into [HomeRowConfigDisplay] for UI purposes
|
||||||
*/
|
*/
|
||||||
private suspend fun resolve(
|
suspend fun resolve(
|
||||||
id: Int,
|
id: Int,
|
||||||
config: HomeRowConfig,
|
config: HomeRowConfig,
|
||||||
): HomeRowConfigDisplay =
|
): HomeRowConfigDisplay =
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.main.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.NonRestartableComposable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.tv.material3.ListItem
|
||||||
|
import androidx.tv.material3.ListItemBorder
|
||||||
|
import androidx.tv.material3.ListItemColors
|
||||||
|
import androidx.tv.material3.ListItemDefaults
|
||||||
|
import androidx.tv.material3.ListItemGlow
|
||||||
|
import androidx.tv.material3.ListItemScale
|
||||||
|
import androidx.tv.material3.ListItemShape
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@NonRestartableComposable
|
||||||
|
fun HomeSettingsListItem(
|
||||||
|
selected: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
headlineContent: @Composable () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
onLongClick: (() -> Unit)? = null,
|
||||||
|
overlineContent: (@Composable () -> Unit)? = null,
|
||||||
|
supportingContent: (@Composable () -> Unit)? = null,
|
||||||
|
leadingContent: (@Composable BoxScope.() -> Unit)? = null,
|
||||||
|
trailingContent: (@Composable () -> Unit)? = null,
|
||||||
|
tonalElevation: Dp = 3.dp,
|
||||||
|
shape: ListItemShape = ListItemDefaults.shape(),
|
||||||
|
colors: ListItemColors = ListItemDefaults.colors(),
|
||||||
|
scale: ListItemScale = ListItemDefaults.scale(),
|
||||||
|
border: ListItemBorder = ListItemDefaults.border(),
|
||||||
|
glow: ListItemGlow = ListItemDefaults.glow(),
|
||||||
|
interactionSource: MutableInteractionSource? = null,
|
||||||
|
) = ListItem(
|
||||||
|
selected = selected,
|
||||||
|
onClick = onClick,
|
||||||
|
headlineContent = headlineContent,
|
||||||
|
modifier = modifier,
|
||||||
|
enabled = enabled,
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
overlineContent = overlineContent,
|
||||||
|
supportingContent = supportingContent,
|
||||||
|
leadingContent = leadingContent,
|
||||||
|
trailingContent = trailingContent,
|
||||||
|
tonalElevation = tonalElevation,
|
||||||
|
shape = shape,
|
||||||
|
colors = colors,
|
||||||
|
scale = scale,
|
||||||
|
border = border,
|
||||||
|
glow = glow,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
)
|
||||||
|
|
@ -12,8 +12,12 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
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.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||||
|
|
@ -23,6 +27,8 @@ import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
||||||
import androidx.navigation3.ui.NavDisplay
|
import androidx.navigation3.ui.NavDisplay
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
|
import com.github.damontecres.wholphin.R
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
||||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||||
import com.github.damontecres.wholphin.ui.main.settings.HomeSettingsDestination.ChooseRowType
|
import com.github.damontecres.wholphin.ui.main.settings.HomeSettingsDestination.ChooseRowType
|
||||||
import com.github.damontecres.wholphin.ui.main.settings.HomeSettingsDestination.RowSettings
|
import com.github.damontecres.wholphin.ui.main.settings.HomeSettingsDestination.RowSettings
|
||||||
|
|
@ -41,6 +47,7 @@ fun HomeSettingsPage(
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val backStack = rememberNavBackStack(HomeSettingsDestination.RowList)
|
val backStack = rememberNavBackStack(HomeSettingsDestination.RowList)
|
||||||
|
var showConfirmDialog by remember { mutableStateOf<(() -> Unit)?>(null) }
|
||||||
|
|
||||||
val state by viewModel.state.collectAsState()
|
val state by viewModel.state.collectAsState()
|
||||||
val discoverEnabled by viewModel.discoverEnabled.collectAsState(false)
|
val discoverEnabled by viewModel.discoverEnabled.collectAsState(false)
|
||||||
|
|
@ -86,7 +93,16 @@ fun HomeSettingsPage(
|
||||||
HomeSettingsRowList(
|
HomeSettingsRowList(
|
||||||
state = state,
|
state = state,
|
||||||
onClickAdd = { backStack.add(HomeSettingsDestination.AddRow) },
|
onClickAdd = { backStack.add(HomeSettingsDestination.AddRow) },
|
||||||
onClickSaveLocal = { viewModel.saveToLocal() },
|
onClickSave = {
|
||||||
|
showConfirmDialog = {
|
||||||
|
viewModel.saveToRemote()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClickLoad = {
|
||||||
|
showConfirmDialog = {
|
||||||
|
viewModel.loadFromRemote()
|
||||||
|
}
|
||||||
|
},
|
||||||
onClickMove = viewModel::moveRow,
|
onClickMove = viewModel::moveRow,
|
||||||
onClickDelete = viewModel::deleteRow,
|
onClickDelete = viewModel::deleteRow,
|
||||||
onClick = { index, row ->
|
onClick = { index, row ->
|
||||||
|
|
@ -189,4 +205,15 @@ fun HomeSettingsPage(
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
showConfirmDialog?.let { onConfirm ->
|
||||||
|
ConfirmDialog(
|
||||||
|
title = stringResource(R.string.confirm),
|
||||||
|
body = "Overwrite?",
|
||||||
|
onCancel = { showConfirmDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
onConfirm.invoke()
|
||||||
|
showConfirmDialog = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
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.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.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
|
@ -27,9 +26,11 @@ import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.focusRestorer
|
import androidx.compose.ui.focus.focusRestorer
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.tv.material3.Icon
|
import androidx.tv.material3.Icon
|
||||||
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.services.HomeRowConfigDisplay
|
import com.github.damontecres.wholphin.services.HomeRowConfigDisplay
|
||||||
|
|
@ -47,7 +48,8 @@ fun HomeSettingsRowList(
|
||||||
state: HomePageSettingsState,
|
state: HomePageSettingsState,
|
||||||
onClick: (Int, HomeRowConfigDisplay) -> Unit,
|
onClick: (Int, HomeRowConfigDisplay) -> Unit,
|
||||||
onClickResize: (Int) -> Unit,
|
onClickResize: (Int) -> Unit,
|
||||||
onClickSaveLocal: () -> Unit,
|
onClickSave: () -> Unit,
|
||||||
|
onClickLoad: () -> Unit,
|
||||||
onClickAdd: () -> Unit,
|
onClickAdd: () -> Unit,
|
||||||
onClickMove: (MoveDirection, Int) -> Unit,
|
onClickMove: (MoveDirection, Int) -> Unit,
|
||||||
onClickDelete: (Int) -> Unit,
|
onClickDelete: (Int) -> Unit,
|
||||||
|
|
@ -82,21 +84,57 @@ fun HomeSettingsRowList(
|
||||||
modifier = Modifier.focusRequester(firstFocus),
|
modifier = Modifier.focusRequester(firstFocus),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
Row {
|
||||||
|
HomeSettingsListItem(
|
||||||
|
selected = false,
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
text = "Increase card sizes",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Edit,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = { onClickResize.invoke(1) },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
HomeSettingsListItem(
|
||||||
|
selected = false,
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
text = "Decrease card sizes",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Edit,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = { onClickResize.invoke(-1) },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
item {
|
item {
|
||||||
HomeSettingsListItem(
|
HomeSettingsListItem(
|
||||||
selected = false,
|
selected = false,
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(
|
Text(
|
||||||
text = "Increase card sizes",
|
text = stringResource(R.string.save_to_server),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(
|
Text(
|
||||||
imageVector = Icons.Default.Edit,
|
text = stringResource(R.string.fa_cloud_arrow_up),
|
||||||
contentDescription = null,
|
fontFamily = FontAwesome,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onClick = { onClickResize.invoke(1) },
|
onClick = onClickSave,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -105,38 +143,26 @@ fun HomeSettingsRowList(
|
||||||
selected = false,
|
selected = false,
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(
|
Text(
|
||||||
text = "Decrease card sizes",
|
text = stringResource(R.string.load_from_server),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Edit,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onClick = { onClickResize.invoke(-1) },
|
|
||||||
modifier = Modifier,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
HomeSettingsListItem(
|
|
||||||
selected = false,
|
|
||||||
headlineContent = {
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.save),
|
text = stringResource(R.string.fa_cloud_arrow_down),
|
||||||
|
fontFamily = FontAwesome,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
leadingContent = {
|
onClick = onClickLoad,
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Create,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onClick = onClickSaveLocal,
|
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.home_rows),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
itemsIndexed(state.rows, key = { _, row -> row.id }) { index, row ->
|
itemsIndexed(state.rows, key = { _, row -> row.id }) { index, row ->
|
||||||
|
|
@ -192,8 +218,7 @@ fun HomeRowConfigContent(
|
||||||
Text(
|
Text(
|
||||||
text = config.title,
|
text = config.title,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier =
|
modifier = Modifier,
|
||||||
Modifier,
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
|
|
||||||
|
|
@ -339,6 +339,52 @@ class HomeSettingsViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveToRemote() {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
serverRepository.currentUser.value?.let { user ->
|
||||||
|
Timber.d("Saving home settings to remote")
|
||||||
|
val rows = state.value.rows.map { it.config }
|
||||||
|
val settings = HomePageSettings(rows = rows)
|
||||||
|
try {
|
||||||
|
Timber.d("saveToRemote")
|
||||||
|
homeSettingsService.saveToServer(user.id, settings)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex)
|
||||||
|
showToast(context, "Error saving: ${ex.localizedMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadFromRemote() {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
serverRepository.currentUser.value?.let { user ->
|
||||||
|
Timber.d("Loading home settings from remote")
|
||||||
|
try {
|
||||||
|
_state.update { it.copy(loading = LoadingState.Loading) }
|
||||||
|
val result = homeSettingsService.loadFromServer(user.id)
|
||||||
|
if (result != null) {
|
||||||
|
Timber.v("Got remote settings")
|
||||||
|
val newRows =
|
||||||
|
result.rows.mapIndexed { index, config ->
|
||||||
|
homeSettingsService.resolve(index, config)
|
||||||
|
}
|
||||||
|
_state.update {
|
||||||
|
it.copy(rows = newRows)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.v("No remote settings")
|
||||||
|
showToast(context, "No server-side settings found")
|
||||||
|
}
|
||||||
|
fetchRowData()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex)
|
||||||
|
showToast(context, "Error: ${ex.localizedMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun saveToLocal() {
|
fun saveToLocal() {
|
||||||
// This uses injected ioScope so that it will still run when the page is closing
|
// This uses injected ioScope so that it will still run when the page is closing
|
||||||
ioScope.launchIO {
|
ioScope.launchIO {
|
||||||
|
|
|
||||||
|
|
@ -50,4 +50,6 @@
|
||||||
<string name="fa_clock" translatable="false"></string>
|
<string name="fa_clock" translatable="false"></string>
|
||||||
<string name="fa_bell" translatable="false"></string>
|
<string name="fa_bell" translatable="false"></string>
|
||||||
<string name="fa_check" translatable="false"></string>
|
<string name="fa_check" translatable="false"></string>
|
||||||
|
<string name="fa_cloud_arrow_up" translatable="false"></string>
|
||||||
|
<string name="fa_cloud_arrow_down" translatable="false"></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -468,6 +468,9 @@
|
||||||
<string name="height">Height</string>
|
<string name="height">Height</string>
|
||||||
<string name="apply_all_rows">Apply to all rows</string>
|
<string name="apply_all_rows">Apply to all rows</string>
|
||||||
<string name="customize_home">Customize home page</string>
|
<string name="customize_home">Customize home page</string>
|
||||||
|
<string name="home_rows">Home rows</string>
|
||||||
|
<string name="load_from_server">Load from server</string>
|
||||||
|
<string name="save_to_server">Save to server</string>
|
||||||
|
|
||||||
<string-array name="theme_song_volume">
|
<string-array name="theme_song_volume">
|
||||||
<item>Disabled</item>
|
<item>Disabled</item>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue