diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/HomeSettingsService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/HomeSettingsService.kt index b6a784b0..6bf03b7e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/HomeSettingsService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/HomeSettingsService.kt @@ -399,7 +399,7 @@ class HomeSettingsService /** * Converts a [HomeRowConfig] into [HomeRowConfigDisplay] for UI purposes */ - private suspend fun resolve( + suspend fun resolve( id: Int, config: HomeRowConfig, ): HomeRowConfigDisplay = diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsListItem.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsListItem.kt new file mode 100644 index 00000000..c9ea14d6 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsListItem.kt @@ -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, +) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsPage.kt index 82ae6f9a..1076d727 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsPage.kt @@ -12,8 +12,12 @@ 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.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator @@ -23,6 +27,8 @@ import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay import androidx.tv.material3.MaterialTheme 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.settings.HomeSettingsDestination.ChooseRowType import com.github.damontecres.wholphin.ui.main.settings.HomeSettingsDestination.RowSettings @@ -41,6 +47,7 @@ fun HomeSettingsPage( val scope = rememberCoroutineScope() val listState = rememberLazyListState() val backStack = rememberNavBackStack(HomeSettingsDestination.RowList) + var showConfirmDialog by remember { mutableStateOf<(() -> Unit)?>(null) } val state by viewModel.state.collectAsState() val discoverEnabled by viewModel.discoverEnabled.collectAsState(false) @@ -86,7 +93,16 @@ fun HomeSettingsPage( HomeSettingsRowList( state = state, onClickAdd = { backStack.add(HomeSettingsDestination.AddRow) }, - onClickSaveLocal = { viewModel.saveToLocal() }, + onClickSave = { + showConfirmDialog = { + viewModel.saveToRemote() + } + }, + onClickLoad = { + showConfirmDialog = { + viewModel.loadFromRemote() + } + }, onClickMove = viewModel::moveRow, onClickDelete = viewModel::deleteRow, onClick = { index, row -> @@ -189,4 +205,15 @@ fun HomeSettingsPage( .weight(1f), ) } + showConfirmDialog?.let { onConfirm -> + ConfirmDialog( + title = stringResource(R.string.confirm), + body = "Overwrite?", + onCancel = { showConfirmDialog = null }, + onConfirm = { + onConfirm.invoke() + showConfirmDialog = null + }, + ) + } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsRowList.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsRowList.kt index 41188e63..9d0cd9f1 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsRowList.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsRowList.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed 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.Edit 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.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign 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 com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.services.HomeRowConfigDisplay @@ -47,7 +48,8 @@ fun HomeSettingsRowList( state: HomePageSettingsState, onClick: (Int, HomeRowConfigDisplay) -> Unit, onClickResize: (Int) -> Unit, - onClickSaveLocal: () -> Unit, + onClickSave: () -> Unit, + onClickLoad: () -> Unit, onClickAdd: () -> Unit, onClickMove: (MoveDirection, Int) -> Unit, onClickDelete: (Int) -> Unit, @@ -82,21 +84,57 @@ fun HomeSettingsRowList( 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 { HomeSettingsListItem( selected = false, headlineContent = { Text( - text = "Increase card sizes", + text = stringResource(R.string.save_to_server), ) }, leadingContent = { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = null, + Text( + text = stringResource(R.string.fa_cloud_arrow_up), + fontFamily = FontAwesome, ) }, - onClick = { onClickResize.invoke(1) }, + onClick = onClickSave, modifier = Modifier, ) } @@ -105,38 +143,26 @@ fun HomeSettingsRowList( selected = false, headlineContent = { Text( - text = "Decrease card sizes", + text = stringResource(R.string.load_from_server), ) }, leadingContent = { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = null, - ) - }, - onClick = { onClickResize.invoke(-1) }, - modifier = Modifier, - ) - } - item { - HomeSettingsListItem( - selected = false, - headlineContent = { Text( - text = stringResource(R.string.save), + text = stringResource(R.string.fa_cloud_arrow_down), + fontFamily = FontAwesome, ) }, - leadingContent = { - Icon( - imageVector = Icons.Default.Create, - contentDescription = null, - ) - }, - onClick = onClickSaveLocal, + onClick = onClickLoad, modifier = Modifier, ) } item { + Text( + text = stringResource(R.string.home_rows), + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth(), + ) HorizontalDivider() } itemsIndexed(state.rows, key = { _, row -> row.id }) { index, row -> @@ -192,8 +218,7 @@ fun HomeRowConfigContent( Text( text = config.title, overflow = TextOverflow.Ellipsis, - modifier = - Modifier, + modifier = Modifier, ) }, onClick = onClick, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsViewModel.kt index ceba6b30..11962c18 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomeSettingsViewModel.kt @@ -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() { // This uses injected ioScope so that it will still run when the page is closing ioScope.launchIO { diff --git a/app/src/main/res/values/fa_strings.xml b/app/src/main/res/values/fa_strings.xml index ae2a4ae1..adf2c254 100644 --- a/app/src/main/res/values/fa_strings.xml +++ b/app/src/main/res/values/fa_strings.xml @@ -50,4 +50,6 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 150d1213..b3050ad8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -468,6 +468,9 @@ Height Apply to all rows Customize home page + Home rows + Load from server + Save to server Disabled