Merge feature/cache-ui: RemoteLibrary cache UI
This commit is contained in:
commit
7205e35d9d
1 changed files with 116 additions and 18 deletions
|
|
@ -8,7 +8,9 @@ package org.dueattendant149.bookshelf.ui.bookshelf
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
|
@ -19,15 +21,21 @@ import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.CloudOff
|
import androidx.compose.material.icons.filled.CloudOff
|
||||||
import androidx.compose.material.icons.filled.CloudQueue
|
import androidx.compose.material.icons.filled.CloudQueue
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.Download
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -39,6 +47,9 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import org.dueattendant149.bookshelf.R
|
import org.dueattendant149.bookshelf.R
|
||||||
|
import org.dueattendant149.bookshelf.domain.model.cache.CacheState
|
||||||
|
import org.dueattendant149.bookshelf.domain.model.cache.CacheStatus
|
||||||
|
import org.dueattendant149.bookshelf.domain.model.library.Book
|
||||||
import org.dueattendant149.bookshelf.presentation.bookshelf.RemoteLibraryModel
|
import org.dueattendant149.bookshelf.presentation.bookshelf.RemoteLibraryModel
|
||||||
import org.dueattendant149.bookshelf.ui.common.components.placeholder.EmptyPlaceholder
|
import org.dueattendant149.bookshelf.ui.common.components.placeholder.EmptyPlaceholder
|
||||||
import org.dueattendant149.bookshelf.ui.common.components.placeholder.ErrorPlaceholder
|
import org.dueattendant149.bookshelf.ui.common.components.placeholder.ErrorPlaceholder
|
||||||
|
|
@ -125,28 +136,34 @@ fun RemoteLibraryContent(model: RemoteLibraryModel) {
|
||||||
modifier = Modifier.padding(bottom = 4.dp)
|
modifier = Modifier.padding(bottom = 4.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.bookshelf_offline_only_label),
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
Switch(
|
||||||
|
checked = state.offlineOnly,
|
||||||
|
onCheckedChange = { model.toggleOfflineOnly() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items(state.books.size) { index ->
|
items(state.books.size) { index ->
|
||||||
val book = state.books[index]
|
val book = state.books[index]
|
||||||
Card(
|
val cacheStatus = state.cacheStatuses[book.remoteId]
|
||||||
modifier = Modifier.fillMaxWidth(),
|
BookListItem(
|
||||||
) {
|
book = book,
|
||||||
ListItem(
|
cacheStatus = cacheStatus,
|
||||||
headlineContent = {
|
onDownload = { model.cacheBook(book) },
|
||||||
Text(
|
onDelete = { model.deleteCache(book) }
|
||||||
text = book.title,
|
|
||||||
style = MaterialTheme.typography.titleMedium
|
|
||||||
)
|
)
|
||||||
},
|
|
||||||
supportingContent = {
|
|
||||||
Text(
|
|
||||||
text = book.author.getAsString()
|
|
||||||
?: stringResource(R.string.unknown_author)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.isLoading && state.libraries.isEmpty() && state.books.isEmpty()) {
|
if (state.isLoading && state.libraries.isEmpty() && state.books.isEmpty()) {
|
||||||
|
|
@ -193,3 +210,84 @@ fun RemoteLibraryContent(model: RemoteLibraryModel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BookListItem(
|
||||||
|
book: Book,
|
||||||
|
cacheStatus: CacheStatus?,
|
||||||
|
onDownload: () -> Unit,
|
||||||
|
onDelete: () -> Unit,
|
||||||
|
) {
|
||||||
|
val cacheState = cacheStatus?.state ?: CacheState.NONE
|
||||||
|
val showDownload = cacheState == CacheState.NONE || cacheState == CacheState.FAILED
|
||||||
|
val showDelete = cacheState != CacheState.NONE
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
ListItem(
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
text = book.title,
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
},
|
||||||
|
supportingContent = {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = book.author.getAsString()
|
||||||
|
?: stringResource(R.string.unknown_author)
|
||||||
|
)
|
||||||
|
if (cacheState != CacheState.NONE) {
|
||||||
|
Text(
|
||||||
|
text = cacheStatusLabel(cacheState),
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trailingContent = {
|
||||||
|
Row {
|
||||||
|
if (showDownload) {
|
||||||
|
IconButton(onClick = onDownload) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Download,
|
||||||
|
contentDescription = stringResource(R.string.bookshelf_download_action)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (showDelete) {
|
||||||
|
IconButton(onClick = onDelete) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Delete,
|
||||||
|
contentDescription = stringResource(R.string.bookshelf_delete_cache_action)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (cacheState == CacheState.DOWNLOADING) {
|
||||||
|
LinearProgressIndicator(
|
||||||
|
progress = { cacheStatus?.progress?.coerceIn(0f, 1f) ?: 0f },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun cacheStatusLabel(state: CacheState): String =
|
||||||
|
when (state) {
|
||||||
|
CacheState.NONE -> ""
|
||||||
|
CacheState.PENDING -> stringResource(R.string.bookshelf_cache_pending)
|
||||||
|
CacheState.DOWNLOADING -> stringResource(R.string.bookshelf_cache_downloading)
|
||||||
|
CacheState.COMPLETED -> stringResource(R.string.bookshelf_cache_completed)
|
||||||
|
CacheState.FAILED -> stringResource(R.string.bookshelf_cache_failed)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue