|
|
@@ -18,6 +18,7 @@ import androidx.compose.foundation.lazy.grid.items
|
|
|
import androidx.compose.foundation.lazy.items
|
|
|
import androidx.compose.material.icons.Icons
|
|
|
import androidx.compose.material.icons.filled.Add
|
|
|
+import androidx.compose.material.icons.filled.Check
|
|
|
import androidx.compose.material.icons.filled.Edit
|
|
|
import androidx.compose.material.icons.filled.FilterList
|
|
|
import androidx.compose.material.icons.filled.MoreVert
|
|
|
@@ -36,6 +37,7 @@ import androidx.compose.runtime.Composable
|
|
|
import androidx.compose.runtime.DisposableEffect
|
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
|
import androidx.compose.runtime.getValue
|
|
|
+import androidx.compose.runtime.mutableIntStateOf
|
|
|
import androidx.compose.runtime.mutableLongStateOf
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
import androidx.compose.runtime.remember
|
|
|
@@ -45,19 +47,22 @@ import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
|
import androidx.compose.ui.platform.LocalContext
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
+import androidx.compose.ui.text.style.TextAlign
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
+import androidx.lifecycle.Lifecycle
|
|
|
+import androidx.lifecycle.LifecycleEventObserver
|
|
|
+import androidx.lifecycle.compose.LocalLifecycleOwner
|
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
|
import io.legado.app.R
|
|
|
-import io.legado.app.data.appDb
|
|
|
import io.legado.app.data.entities.Book
|
|
|
import io.legado.app.data.entities.BookChapter
|
|
|
import io.legado.app.data.entities.BookGroup
|
|
|
import io.legado.app.data.entities.BookSource
|
|
|
import io.legado.app.data.entities.SearchBook
|
|
|
import io.legado.app.domain.usecase.ChangeSourceMigrationOptions
|
|
|
-import io.legado.app.help.book.isSameNameAuthor
|
|
|
import io.legado.app.ui.book.changecover.ChangeCoverViewModel
|
|
|
import io.legado.app.ui.book.changesource.ChangeBookSourceComposeViewModel
|
|
|
+import io.legado.app.ui.book.changesource.ChangeSourceConfig
|
|
|
import io.legado.app.ui.book.changesource.ChangeSourceMigrationOptionsSheet
|
|
|
import io.legado.app.ui.book.group.GroupEditSheet
|
|
|
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
|
|
@@ -65,6 +70,7 @@ import io.legado.app.ui.book.source.manage.BookSourceActivity
|
|
|
import io.legado.app.ui.theme.LegadoTheme
|
|
|
import io.legado.app.ui.widget.components.AppLinearProgressIndicator
|
|
|
import io.legado.app.ui.widget.components.AppTextField
|
|
|
+import io.legado.app.ui.widget.components.EmptyMessage
|
|
|
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
|
|
import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow
|
|
|
import io.legado.app.ui.widget.components.button.MediumIconButton
|
|
|
@@ -76,6 +82,7 @@ import io.legado.app.ui.widget.components.cover.CoilBookCover
|
|
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
|
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
|
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
|
|
+import io.legado.app.ui.widget.components.tabRow.AppTabRow
|
|
|
import io.legado.app.ui.widget.components.text.AppText
|
|
|
import io.legado.app.utils.StartActivityContract
|
|
|
import io.legado.app.utils.startActivity
|
|
|
@@ -118,11 +125,11 @@ fun WebFileSheet(
|
|
|
@Composable
|
|
|
fun GroupSelectSheet(
|
|
|
show: Boolean,
|
|
|
+ groups: List<BookGroup>,
|
|
|
currentGroupId: Long,
|
|
|
onDismissRequest: () -> Unit,
|
|
|
onConfirm: (Long) -> Unit,
|
|
|
) {
|
|
|
- val groups by appDb.bookGroupDao.flowSelect().collectAsStateWithLifecycle(initialValue = emptyList())
|
|
|
var selectedGroupId by remember(currentGroupId) { mutableLongStateOf(currentGroupId) }
|
|
|
var editingGroup by remember { mutableStateOf<BookGroup?>(null) }
|
|
|
|
|
|
@@ -138,7 +145,7 @@ fun GroupSelectSheet(
|
|
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
|
) {
|
|
|
items(groups, key = { it.groupId }) { group ->
|
|
|
- val isSelected = selectedGroupId and group.groupId > 0
|
|
|
+ val isSelected = (selectedGroupId and group.groupId) != 0L
|
|
|
SelectionItemCard(
|
|
|
title = group.groupName,
|
|
|
isSelected = isSelected,
|
|
|
@@ -194,7 +201,7 @@ fun ChangeCoverSheet(
|
|
|
onSelect: (String) -> Unit,
|
|
|
viewModel: ChangeCoverViewModel = koinViewModel(key = "cover-$name-$author"),
|
|
|
) {
|
|
|
- val items by viewModel.dataFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
|
|
+ val items by viewModel.dataFlow.collectAsStateWithLifecycle(initialValue = emptyList<SearchBook>())
|
|
|
val isSearching by viewModel.isSearching.collectAsStateWithLifecycle()
|
|
|
|
|
|
LaunchedEffect(name, author) {
|
|
|
@@ -244,22 +251,25 @@ fun ChangeSourceSheet(
|
|
|
viewModel: ChangeBookSourceComposeViewModel = koinViewModel(key = "source-${oldBook.bookUrl}"),
|
|
|
) {
|
|
|
val context = LocalContext.current
|
|
|
- val items by viewModel.searchDataFlow.collectAsStateWithLifecycle(initialValue = emptyList())
|
|
|
+ val lifecycleOwner = LocalLifecycleOwner.current
|
|
|
+ val items by viewModel.searchDataFlow.collectAsStateWithLifecycle(initialValue = emptyList<SearchBook>())
|
|
|
val isSearching by viewModel.isSearching.collectAsStateWithLifecycle()
|
|
|
val progress by viewModel.changeSourceProgress.collectAsStateWithLifecycle()
|
|
|
- val groups by appDb.bookSourceDao.flowEnabledGroups().collectAsStateWithLifecycle(initialValue = emptyList())
|
|
|
+ val groups by viewModel.enabledGroups.collectAsStateWithLifecycle(initialValue = emptyList<String>())
|
|
|
+ val enabledSources by viewModel.enabledSources.collectAsStateWithLifecycle(initialValue = emptyList<io.legado.app.data.entities.BookSourcePart>())
|
|
|
+ val scopeState by viewModel.scopeUiState.collectAsStateWithLifecycle()
|
|
|
var searchQuery by rememberSaveable { mutableStateOf("") }
|
|
|
- val selectedGroup = viewModel.searchGroup
|
|
|
val checkAuthor = viewModel.checkAuthor
|
|
|
val loadInfo = viewModel.loadInfo
|
|
|
val loadToc = viewModel.loadToc
|
|
|
val loadWordCount = viewModel.loadWordCount
|
|
|
var actionBook by remember { mutableStateOf<SearchBook?>(null) }
|
|
|
var mismatchBook by remember { mutableStateOf<SearchBook?>(null) }
|
|
|
- var pendingMigration by remember { mutableStateOf<PendingSourceMigration?>(null) }
|
|
|
+ var showMigrationOptions by remember { mutableStateOf(false) }
|
|
|
var loadingAction by remember { mutableStateOf(false) }
|
|
|
var showOptionsMenu by rememberSaveable { mutableStateOf(false) }
|
|
|
- var showFilterMenu by rememberSaveable { mutableStateOf(false) }
|
|
|
+ var showFilterSheet by rememberSaveable { mutableStateOf(false) }
|
|
|
+ var scopeSheetTab by rememberSaveable { mutableIntStateOf(0) }
|
|
|
val bookAddedToShelfText = stringResource(R.string.book_added_to_shelf)
|
|
|
|
|
|
val editSourceResult = rememberLauncherForActivityResult(StartActivityContract(BookSourceEditActivity::class.java)) {
|
|
|
@@ -270,6 +280,21 @@ fun ChangeSourceSheet(
|
|
|
LaunchedEffect(oldBook.bookUrl) {
|
|
|
viewModel.initData(oldBook.name, oldBook.author, oldBook, false)
|
|
|
}
|
|
|
+
|
|
|
+ DisposableEffect(lifecycleOwner, viewModel) {
|
|
|
+ val observer = LifecycleEventObserver { _, event ->
|
|
|
+ when (event) {
|
|
|
+ Lifecycle.Event.ON_RESUME -> viewModel.resume()
|
|
|
+ Lifecycle.Event.ON_PAUSE -> viewModel.pause()
|
|
|
+ else -> Unit
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lifecycleOwner.lifecycle.addObserver(observer)
|
|
|
+ onDispose {
|
|
|
+ lifecycleOwner.lifecycle.removeObserver(observer)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
DisposableEffect(oldBook.bookUrl) {
|
|
|
onDispose {
|
|
|
viewModel.stopSearch()
|
|
|
@@ -323,10 +348,17 @@ fun ChangeSourceSheet(
|
|
|
dismiss()
|
|
|
}
|
|
|
)
|
|
|
+ RoundDropdownMenuItem(
|
|
|
+ text = stringResource(R.string.book_source_manage),
|
|
|
+ onClick = {
|
|
|
+ context.startActivity<BookSourceActivity>()
|
|
|
+ dismiss()
|
|
|
+ }
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
MediumIconButton(
|
|
|
- onClick = { context.startActivity<BookSourceActivity>() },
|
|
|
+ onClick = { showMigrationOptions = true },
|
|
|
imageVector = Icons.Outlined.Settings
|
|
|
)
|
|
|
}
|
|
|
@@ -337,35 +369,10 @@ fun ChangeSourceSheet(
|
|
|
onClick = { viewModel.startOrStopSearch() },
|
|
|
imageVector = if (isSearching) Icons.Default.PauseCircleOutline else Icons.Default.Refresh,
|
|
|
)
|
|
|
- Box {
|
|
|
- MediumIconButton(
|
|
|
- onClick = { showFilterMenu = true },
|
|
|
- imageVector = Icons.Default.FilterList
|
|
|
- )
|
|
|
- RoundDropdownMenu(
|
|
|
- expanded = showFilterMenu,
|
|
|
- onDismissRequest = { showFilterMenu = false }
|
|
|
- ) { dismiss ->
|
|
|
- RoundDropdownMenuItem(
|
|
|
- text = stringResource(R.string.all_source),
|
|
|
- isSelected = selectedGroup.isBlank(),
|
|
|
- onClick = {
|
|
|
- viewModel.onSearchGroupSelected("")
|
|
|
- dismiss()
|
|
|
- }
|
|
|
- )
|
|
|
- groups.forEach { group ->
|
|
|
- RoundDropdownMenuItem(
|
|
|
- text = group,
|
|
|
- isSelected = selectedGroup == group,
|
|
|
- onClick = {
|
|
|
- viewModel.onSearchGroupSelected(group)
|
|
|
- dismiss()
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ MediumIconButton(
|
|
|
+ onClick = { showFilterSheet = true },
|
|
|
+ imageVector = Icons.Default.FilterList
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
) {
|
|
|
@@ -389,94 +396,115 @@ fun ChangeSourceSheet(
|
|
|
)
|
|
|
Spacer(modifier = Modifier.height(12.dp))
|
|
|
}
|
|
|
- LazyColumn(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
|
|
- items(items, key = { it.bookUrl + it.origin }) { item ->
|
|
|
- val bookScore by remember(item.origin, item.name, item.author) {
|
|
|
- viewModel.bookScoreFlow(item)
|
|
|
- }.collectAsStateWithLifecycle()
|
|
|
- SelectionItemCard(
|
|
|
- title = item.originName,
|
|
|
- containerColor = LegadoTheme.colorScheme.onSheetContent,
|
|
|
- selectedContainerColor = LegadoTheme.colorScheme.primaryContainer.copy(alpha = 0.32f),
|
|
|
- leadingContent = {
|
|
|
- MediumIconButton(
|
|
|
- onClick = {
|
|
|
- viewModel.onBookScoreClick(item)
|
|
|
- },
|
|
|
- imageVector = Icons.Default.PushPin,
|
|
|
- tint = if (bookScore > 0) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.outline,
|
|
|
- contentDescription = null
|
|
|
- )
|
|
|
- },
|
|
|
- supportingContent = {
|
|
|
- Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
|
- AppText(
|
|
|
- text = item.author,
|
|
|
- style = LegadoTheme.typography.labelLargeEmphasized
|
|
|
- )
|
|
|
- AppText(
|
|
|
- text = item.getDisplayLastChapterTitle(),
|
|
|
- style = LegadoTheme.typography.labelMediumEmphasized
|
|
|
+
|
|
|
+ if (items.isEmpty()) {
|
|
|
+ Box(
|
|
|
+ modifier = Modifier.fillMaxWidth().padding(vertical = 40.dp),
|
|
|
+ contentAlignment = Alignment.Center
|
|
|
+ ) {
|
|
|
+ EmptyMessage(
|
|
|
+ message = stringResource(R.string.search_empty)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ LazyColumn(
|
|
|
+ modifier = Modifier.fillMaxWidth(),
|
|
|
+ verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
|
+ ) {
|
|
|
+ items(items, key = { it.bookUrl + it.origin }) { item ->
|
|
|
+ val bookScore by remember(item.origin, item.name, item.author) {
|
|
|
+ viewModel.bookScoreFlow(item)
|
|
|
+ }.collectAsStateWithLifecycle()
|
|
|
+ SelectionItemCard(
|
|
|
+ title = item.originName,
|
|
|
+ containerColor = LegadoTheme.colorScheme.onSheetContent,
|
|
|
+ selectedContainerColor = LegadoTheme.colorScheme.primaryContainer.copy(alpha = 0.32f),
|
|
|
+ leadingContent = {
|
|
|
+ MediumIconButton(
|
|
|
+ onClick = {
|
|
|
+ viewModel.onBookScoreClick(item)
|
|
|
+ },
|
|
|
+ imageVector = Icons.Default.PushPin,
|
|
|
+ tint = if (bookScore > 0) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.outline,
|
|
|
+ contentDescription = null
|
|
|
)
|
|
|
- item.chapterWordCountText?.takeIf { loadWordCount }?.let {
|
|
|
+ },
|
|
|
+ supportingContent = {
|
|
|
+ Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
|
AppText(
|
|
|
- text = it,
|
|
|
- style = LegadoTheme.typography.labelSmallEmphasized,
|
|
|
- color = LegadoTheme.colorScheme.primary
|
|
|
+ text = item.author,
|
|
|
+ style = LegadoTheme.typography.labelLargeEmphasized
|
|
|
)
|
|
|
+ AppText(
|
|
|
+ text = item.getDisplayLastChapterTitle(),
|
|
|
+ style = LegadoTheme.typography.labelMediumEmphasized
|
|
|
+ )
|
|
|
+ item.chapterWordCountText?.takeIf { loadWordCount }?.let {
|
|
|
+ AppText(
|
|
|
+ text = it,
|
|
|
+ style = LegadoTheme.typography.labelSmallEmphasized,
|
|
|
+ color = LegadoTheme.colorScheme.primary
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- },
|
|
|
- isSelected = item.bookUrl == oldBook.bookUrl,
|
|
|
- onToggleSelection = {
|
|
|
- if (item.bookUrl != oldBook.bookUrl) {
|
|
|
- if (!item.sameBookTypeLocal(oldBook.type)) mismatchBook = item else actionBook = item
|
|
|
- }
|
|
|
- },
|
|
|
- dropdownContent = { onDismiss: () -> Unit ->
|
|
|
- RoundDropdownMenuItem(
|
|
|
- text = stringResource(R.string.to_top),
|
|
|
- onClick = {
|
|
|
- viewModel.topSource(item)
|
|
|
- onDismiss()
|
|
|
- }
|
|
|
- )
|
|
|
- RoundDropdownMenuItem(
|
|
|
- text = "置底",
|
|
|
- onClick = {
|
|
|
- viewModel.bottomSource(item)
|
|
|
- onDismiss()
|
|
|
- }
|
|
|
- )
|
|
|
- RoundDropdownMenuItem(
|
|
|
- text = stringResource(R.string.edit),
|
|
|
- onClick = {
|
|
|
- onDismiss()
|
|
|
- editSourceResult.launch { putExtra("sourceUrl", item.origin) }
|
|
|
- }
|
|
|
- )
|
|
|
- RoundDropdownMenuItem(
|
|
|
- text = "禁用",
|
|
|
- onClick = {
|
|
|
- viewModel.disableSource(item)
|
|
|
- onDismiss()
|
|
|
+ },
|
|
|
+ isSelected = item.bookUrl == oldBook.bookUrl,
|
|
|
+ onToggleSelection = {
|
|
|
+ if (item.bookUrl != oldBook.bookUrl) {
|
|
|
+ if (!item.sameBookTypeLocal(oldBook.type)) mismatchBook = item else actionBook =
|
|
|
+ item
|
|
|
}
|
|
|
- )
|
|
|
- RoundDropdownMenuItem(
|
|
|
- text = stringResource(R.string.delete),
|
|
|
- color = LegadoTheme.colorScheme.error,
|
|
|
- onClick = {
|
|
|
- viewModel.del(item)
|
|
|
- if (oldBook.bookUrl == item.bookUrl) {
|
|
|
- viewModel.autoChangeSource(oldBook.type) { book, toc, source ->
|
|
|
- pendingMigration = PendingSourceMigration(source, book, toc)
|
|
|
+ },
|
|
|
+ dropdownContent = { onDismiss: () -> Unit ->
|
|
|
+ RoundDropdownMenuItem(
|
|
|
+ text = stringResource(R.string.to_top),
|
|
|
+ onClick = {
|
|
|
+ viewModel.topSource(item)
|
|
|
+ onDismiss()
|
|
|
+ }
|
|
|
+ )
|
|
|
+ RoundDropdownMenuItem(
|
|
|
+ text = "置底",
|
|
|
+ onClick = {
|
|
|
+ viewModel.bottomSource(item)
|
|
|
+ onDismiss()
|
|
|
+ }
|
|
|
+ )
|
|
|
+ RoundDropdownMenuItem(
|
|
|
+ text = stringResource(R.string.edit),
|
|
|
+ onClick = {
|
|
|
+ onDismiss()
|
|
|
+ editSourceResult.launch { putExtra("sourceUrl", item.origin) }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ RoundDropdownMenuItem(
|
|
|
+ text = "禁用",
|
|
|
+ onClick = {
|
|
|
+ viewModel.disableSource(item)
|
|
|
+ onDismiss()
|
|
|
+ }
|
|
|
+ )
|
|
|
+ RoundDropdownMenuItem(
|
|
|
+ text = stringResource(R.string.delete),
|
|
|
+ color = LegadoTheme.colorScheme.error,
|
|
|
+ onClick = {
|
|
|
+ viewModel.del(item)
|
|
|
+ if (oldBook.bookUrl == item.bookUrl) {
|
|
|
+ viewModel.autoChangeSource(oldBook.type) { book, toc, source ->
|
|
|
+ onReplace(
|
|
|
+ source,
|
|
|
+ book,
|
|
|
+ toc,
|
|
|
+ ChangeSourceConfig.getMigrationOptions()
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
+ onDismiss()
|
|
|
}
|
|
|
- onDismiss()
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
- )
|
|
|
+ )
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
|
@@ -485,19 +513,24 @@ fun ChangeSourceSheet(
|
|
|
val performAction: (SearchBook, Boolean) -> Unit = { searchBook, replace ->
|
|
|
loadingAction = true
|
|
|
val book = viewModel.bookMap[searchBook.primaryStr()] ?: searchBook.toBook()
|
|
|
- viewModel.getToc(book, { toc, source ->
|
|
|
- loadingAction = false
|
|
|
- if (replace) {
|
|
|
- pendingMigration = PendingSourceMigration(source, book, toc)
|
|
|
- } else {
|
|
|
- onAddAsNew(book, toc)
|
|
|
- context.toastOnUi(bookAddedToShelfText)
|
|
|
+ viewModel.getToc(
|
|
|
+ book,
|
|
|
+ onSuccess = { toc, source ->
|
|
|
+ loadingAction = false
|
|
|
+ if (replace) {
|
|
|
+ onReplace(source, book, toc, ChangeSourceConfig.getMigrationOptions())
|
|
|
+ onDismissRequest()
|
|
|
+ } else {
|
|
|
+ onAddAsNew(book, toc)
|
|
|
+ context.toastOnUi(bookAddedToShelfText)
|
|
|
+ }
|
|
|
+ actionBook = null
|
|
|
+ },
|
|
|
+ onError = {
|
|
|
+ loadingAction = false
|
|
|
+ context.toastOnUi(if (replace) "换源失败" else "添加书籍失败")
|
|
|
}
|
|
|
- actionBook = null
|
|
|
- }, {
|
|
|
- loadingAction = false
|
|
|
- context.toastOnUi(if (replace) "换源失败" else "添加书籍失败")
|
|
|
- })
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
AppAlertDialog(
|
|
|
@@ -531,30 +564,103 @@ fun ChangeSourceSheet(
|
|
|
}
|
|
|
}
|
|
|
)
|
|
|
- val migration = pendingMigration
|
|
|
ChangeSourceMigrationOptionsSheet(
|
|
|
- show = migration != null,
|
|
|
+ show = showMigrationOptions,
|
|
|
title = "换源选项",
|
|
|
- subtitle = migration?.let {
|
|
|
- val sameNameAuthor = oldBook.isSameNameAuthor(it.book)
|
|
|
- if (sameNameAuthor && oldBook.origin != it.book.origin) {
|
|
|
- "检测到书名、作者相同但书源不同,可选择本次要迁移的数据。"
|
|
|
- } else {
|
|
|
- "选择本次替换当前书籍时要迁移的数据。"
|
|
|
- }
|
|
|
- },
|
|
|
- onDismissRequest = { pendingMigration = null },
|
|
|
+ onDismissRequest = { showMigrationOptions = false },
|
|
|
onConfirm = { options ->
|
|
|
- val pending = pendingMigration ?: return@ChangeSourceMigrationOptionsSheet
|
|
|
- onReplace(pending.source, pending.book, pending.toc, options)
|
|
|
- pendingMigration = null
|
|
|
- onDismissRequest()
|
|
|
+ ChangeSourceConfig.setMigrationOptions(options)
|
|
|
+ showMigrationOptions = false
|
|
|
}
|
|
|
)
|
|
|
+
|
|
|
+ AppModalBottomSheet(
|
|
|
+ show = showFilterSheet,
|
|
|
+ onDismissRequest = { showFilterSheet = false },
|
|
|
+ title = stringResource(R.string.search_select_group),
|
|
|
+ endAction = {
|
|
|
+ MediumIconButton(
|
|
|
+ onClick = {
|
|
|
+ viewModel.startSearch()
|
|
|
+ showFilterSheet = false
|
|
|
+ },
|
|
|
+ imageVector = Icons.Default.Check
|
|
|
+ )
|
|
|
+ }
|
|
|
+ ) {
|
|
|
+ Column {
|
|
|
+ SelectionItemCard(
|
|
|
+ title = stringResource(R.string.all_source),
|
|
|
+ isSelected = scopeState.isAll,
|
|
|
+ containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f),
|
|
|
+ inSelectionMode = true,
|
|
|
+ onToggleSelection = {
|
|
|
+ viewModel.selectAllScope()
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ Spacer(modifier = Modifier.height(8.dp))
|
|
|
+ AppTabRow(
|
|
|
+ tabTitles = listOf(
|
|
|
+ stringResource(R.string.group),
|
|
|
+ stringResource(R.string.book_source),
|
|
|
+ ),
|
|
|
+ selectedTabIndex = scopeSheetTab,
|
|
|
+ onTabSelected = { scopeSheetTab = it },
|
|
|
+ )
|
|
|
+ Spacer(modifier = Modifier.height(6.dp))
|
|
|
+
|
|
|
+ if (scopeSheetTab == 0) {
|
|
|
+ LazyColumn(
|
|
|
+ verticalArrangement = Arrangement.spacedBy(4.dp)
|
|
|
+ ) {
|
|
|
+ items(groups, key = { it }) {
|
|
|
+ val selected = !scopeState.isSource && scopeState.displayNames.contains(it)
|
|
|
+ SelectionItemCard(
|
|
|
+ title = it,
|
|
|
+ isSelected = selected,
|
|
|
+ containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f),
|
|
|
+ inSelectionMode = true,
|
|
|
+ onToggleSelection = {
|
|
|
+ viewModel.toggleScopeGroup(it)
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (enabledSources.isEmpty()) {
|
|
|
+ Text(
|
|
|
+ text = stringResource(R.string.search_empty),
|
|
|
+ style = LegadoTheme.typography.bodyMedium,
|
|
|
+ color = LegadoTheme.colorScheme.onSurfaceVariant,
|
|
|
+ textAlign = TextAlign.Center,
|
|
|
+ modifier = Modifier
|
|
|
+ .fillMaxWidth()
|
|
|
+ .padding(vertical = 12.dp),
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ LazyColumn(
|
|
|
+ verticalArrangement = Arrangement.spacedBy(4.dp)
|
|
|
+ ) {
|
|
|
+ items(enabledSources, key = { it.bookSourceUrl }) {
|
|
|
+ val selected = scopeState.sourceUrls.contains(it.bookSourceUrl)
|
|
|
+ SelectionItemCard(
|
|
|
+ title = it.bookSourceName,
|
|
|
+ subtitle = it.bookSourceGroup?.takeIf { group -> group.isNotBlank() },
|
|
|
+ containerColor = LegadoTheme.colorScheme.surface.copy(alpha = 0.6f),
|
|
|
+ isSelected = selected,
|
|
|
+ inSelectionMode = true,
|
|
|
+ onToggleSelection = {
|
|
|
+ viewModel.toggleScopeSource(it)
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Spacer(modifier = Modifier.height(20.dp))
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-private data class PendingSourceMigration(
|
|
|
- val source: BookSource,
|
|
|
- val book: Book,
|
|
|
- val toc: List<BookChapter>,
|
|
|
-)
|