[优化] 新增书架界面的书籍管理
This commit is contained in:
@@ -54,6 +54,7 @@ fun BookshelfItem(
|
|||||||
cover: @Composable (Modifier) -> Unit,
|
cover: @Composable (Modifier) -> Unit,
|
||||||
title: String,
|
title: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
isSelected: Boolean = false,
|
||||||
subTitle: String? = null,
|
subTitle: String? = null,
|
||||||
desc: String? = null,
|
desc: String? = null,
|
||||||
extra: @Composable (RowScope.() -> Unit)? = null,
|
extra: @Composable (RowScope.() -> Unit)? = null,
|
||||||
@@ -64,10 +65,16 @@ fun BookshelfItem(
|
|||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit
|
onLongClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
val selectedColor = if (isSelected) {
|
||||||
|
LegadoTheme.colorScheme.secondaryContainer.copy(alpha = 0.55f)
|
||||||
|
} else {
|
||||||
|
Color.Transparent
|
||||||
|
}
|
||||||
if (isGrid) {
|
if (isGrid) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clip(MaterialTheme.shapes.small)
|
.clip(MaterialTheme.shapes.small)
|
||||||
|
.background(selectedColor)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick
|
onLongClick = onLongClick
|
||||||
@@ -138,6 +145,8 @@ fun BookshelfItem(
|
|||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.clip(MaterialTheme.shapes.small)
|
||||||
|
.background(selectedColor)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick
|
onLongClick = onLongClick
|
||||||
@@ -377,6 +386,7 @@ fun BookItem(
|
|||||||
book: BookShelfItem,
|
book: BookShelfItem,
|
||||||
layoutMode: Int,
|
layoutMode: Int,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
isSelected: Boolean = false,
|
||||||
gridStyle: Int = 0,
|
gridStyle: Int = 0,
|
||||||
isCompact: Boolean = false,
|
isCompact: Boolean = false,
|
||||||
isUpdating: Boolean = false,
|
isUpdating: Boolean = false,
|
||||||
@@ -404,6 +414,7 @@ fun BookItem(
|
|||||||
isGrid = layoutMode != 0,
|
isGrid = layoutMode != 0,
|
||||||
gridStyle = gridStyle,
|
gridStyle = gridStyle,
|
||||||
isCompact = isCompact,
|
isCompact = isCompact,
|
||||||
|
isSelected = isSelected,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
cover = { modifier ->
|
cover = { modifier ->
|
||||||
BookshelfCover(
|
BookshelfCover(
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import androidx.compose.foundation.pager.HorizontalPager
|
|||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.FormatListBulleted
|
import androidx.compose.material.icons.automirrored.filled.FormatListBulleted
|
||||||
|
import androidx.compose.material.icons.filled.Bookmarks
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Download
|
import androidx.compose.material.icons.filled.Download
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
@@ -32,7 +33,7 @@ import androidx.compose.material.icons.filled.ImportExport
|
|||||||
import androidx.compose.material.icons.filled.Link
|
import androidx.compose.material.icons.filled.Link
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material.icons.filled.Save
|
import androidx.compose.material.icons.filled.Save
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.SelectAll
|
||||||
import androidx.compose.material.icons.filled.Wifi
|
import androidx.compose.material.icons.filled.Wifi
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -67,7 +68,7 @@ import io.legado.app.R
|
|||||||
import io.legado.app.base.BaseRuleEvent
|
import io.legado.app.base.BaseRuleEvent
|
||||||
import io.legado.app.ui.about.AppLogSheet
|
import io.legado.app.ui.about.AppLogSheet
|
||||||
import io.legado.app.ui.book.cache.CacheActivity
|
import io.legado.app.ui.book.cache.CacheActivity
|
||||||
import io.legado.app.ui.book.manage.BookshelfManageActivity
|
import io.legado.app.ui.book.info.GroupSelectSheet
|
||||||
import io.legado.app.ui.book.search.SearchActivity
|
import io.legado.app.ui.book.search.SearchActivity
|
||||||
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
@@ -77,8 +78,10 @@ import io.legado.app.ui.theme.adaptiveHorizontalPadding
|
|||||||
import io.legado.app.ui.theme.adaptiveHorizontalPaddingTab
|
import io.legado.app.ui.theme.adaptiveHorizontalPaddingTab
|
||||||
import io.legado.app.ui.widget.components.EmptyMessage
|
import io.legado.app.ui.widget.components.EmptyMessage
|
||||||
import io.legado.app.ui.widget.components.button.SmallOutlinedIconToggleButton
|
import io.legado.app.ui.widget.components.button.SmallOutlinedIconToggleButton
|
||||||
|
import io.legado.app.ui.widget.components.button.TopBarActionButton
|
||||||
import io.legado.app.ui.widget.components.card.NormalCard
|
import io.legado.app.ui.widget.components.card.NormalCard
|
||||||
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
|
||||||
|
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||||
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
|
||||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyVerticalGrid
|
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyVerticalGrid
|
||||||
import io.legado.app.ui.widget.components.list.ListScaffold
|
import io.legado.app.ui.widget.components.list.ListScaffold
|
||||||
@@ -117,6 +120,9 @@ fun BookshelfScreen(
|
|||||||
var showGroupManageSheet by remember { mutableStateOf(false) }
|
var showGroupManageSheet by remember { mutableStateOf(false) }
|
||||||
var showLogSheet by remember { mutableStateOf(false) }
|
var showLogSheet by remember { mutableStateOf(false) }
|
||||||
var showGroupMenu by remember { mutableStateOf(false) }
|
var showGroupMenu by remember { mutableStateOf(false) }
|
||||||
|
var showGroupSelectSheet by remember { mutableStateOf(false) }
|
||||||
|
var isEditMode by remember { mutableStateOf(false) }
|
||||||
|
var selectedBookUrls by remember { mutableStateOf<Set<String>>(emptySet()) }
|
||||||
|
|
||||||
val clipboardManager = LocalClipboard.current
|
val clipboardManager = LocalClipboard.current
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
@@ -202,6 +208,50 @@ fun BookshelfScreen(
|
|||||||
val bookGroupStyle = BookshelfConfig.bookGroupStyle
|
val bookGroupStyle = BookshelfConfig.bookGroupStyle
|
||||||
// 控制是否处于“文件夹列表”根视图,还是“文件夹内部”书籍视图
|
// 控制是否处于“文件夹列表”根视图,还是“文件夹内部”书籍视图
|
||||||
var isInFolderRoot by remember(bookGroupStyle) { mutableStateOf(bookGroupStyle == 2) }
|
var isInFolderRoot by remember(bookGroupStyle) { mutableStateOf(bookGroupStyle == 2) }
|
||||||
|
|
||||||
|
val clearSelection = {
|
||||||
|
selectedBookUrls = emptySet()
|
||||||
|
}
|
||||||
|
val exitEditMode = {
|
||||||
|
isEditMode = false
|
||||||
|
clearSelection()
|
||||||
|
}
|
||||||
|
val toggleEditMode = {
|
||||||
|
if (isEditMode) {
|
||||||
|
exitEditMode()
|
||||||
|
} else {
|
||||||
|
if (bookGroupStyle == 2 && isInFolderRoot) {
|
||||||
|
isInFolderRoot = false
|
||||||
|
}
|
||||||
|
isEditMode = true
|
||||||
|
clearSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val toggleBookSelection: (String) -> Unit = { bookUrl ->
|
||||||
|
selectedBookUrls = if (selectedBookUrls.contains(bookUrl)) {
|
||||||
|
selectedBookUrls - bookUrl
|
||||||
|
} else {
|
||||||
|
selectedBookUrls + bookUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(pagerState.currentPage, isInFolderRoot) {
|
||||||
|
clearSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(uiState.items) {
|
||||||
|
val visibleBookUrls = uiState.items.mapTo(hashSetOf()) { it.bookUrl }
|
||||||
|
selectedBookUrls = selectedBookUrls.intersect(visibleBookUrls)
|
||||||
|
}
|
||||||
|
|
||||||
|
BackHandler(enabled = isEditMode) {
|
||||||
|
if (selectedBookUrls.isNotEmpty()) {
|
||||||
|
clearSelection()
|
||||||
|
} else {
|
||||||
|
exitEditMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val baseTitle = when (bookGroupStyle) {
|
val baseTitle = when (bookGroupStyle) {
|
||||||
1 -> {
|
1 -> {
|
||||||
uiState.groups.getOrNull(pagerState.currentPage)?.groupName
|
uiState.groups.getOrNull(pagerState.currentPage)?.groupName
|
||||||
@@ -221,7 +271,7 @@ fun BookshelfScreen(
|
|||||||
baseTitle
|
baseTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bookGroupStyle == 2 && !isInFolderRoot) {
|
if (bookGroupStyle == 2 && !isInFolderRoot && !isEditMode) {
|
||||||
BackHandler {
|
BackHandler {
|
||||||
isInFolderRoot = true
|
isInFolderRoot = true
|
||||||
}
|
}
|
||||||
@@ -243,85 +293,113 @@ fun BookshelfScreen(
|
|||||||
ListScaffold(
|
ListScaffold(
|
||||||
title = title,
|
title = title,
|
||||||
state = uiState,
|
state = uiState,
|
||||||
|
onBackClick = if (isEditMode) exitEditMode else null,
|
||||||
|
backNavigationIcon = if (isEditMode) AppIcons.Close else AppIcons.Back,
|
||||||
|
showSearchAction = !isEditMode,
|
||||||
onSearchToggle = { context.startActivity<SearchActivity>() },
|
onSearchToggle = { context.startActivity<SearchActivity>() },
|
||||||
onSearchQueryChange = { viewModel.setSearchKey(it) },
|
onSearchQueryChange = { viewModel.setSearchKey(it) },
|
||||||
topBarActions = { },
|
topBarActions = {
|
||||||
dropDownMenuContent = { dismiss ->
|
if (isEditMode) {
|
||||||
RoundDropdownMenuItem(
|
TopBarActionButton(
|
||||||
text = stringResource(R.string.add_remote_book),
|
onClick = {
|
||||||
onClick = { onNavigateToRemoteImport(); dismiss() },
|
selectedBookUrls = uiState.items.mapTo(hashSetOf()) { it.bookUrl }
|
||||||
leadingIcon = { Icon(Icons.Default.Wifi, null) }
|
},
|
||||||
)
|
imageVector = Icons.Default.SelectAll,
|
||||||
RoundDropdownMenuItem(
|
contentDescription = stringResource(R.string.select_all)
|
||||||
text = stringResource(R.string.book_local),
|
)
|
||||||
onClick = { onNavigateToLocalImport(); dismiss() },
|
TopBarActionButton(
|
||||||
leadingIcon = { Icon(Icons.Default.Save, null) }
|
onClick = {
|
||||||
)
|
val visibleBookUrls = uiState.items.mapTo(hashSetOf()) { it.bookUrl }
|
||||||
RoundDropdownMenuItem(
|
selectedBookUrls = visibleBookUrls - selectedBookUrls
|
||||||
text = stringResource(R.string.update_toc),
|
},
|
||||||
onClick = { viewModel.upToc(uiState.items); dismiss() },
|
imageVector = Icons.Default.Refresh,
|
||||||
leadingIcon = { Icon(Icons.Default.Refresh, null) }
|
contentDescription = stringResource(R.string.revert_selection)
|
||||||
)
|
)
|
||||||
RoundDropdownMenuItem(
|
TopBarActionButton(
|
||||||
text = stringResource(R.string.layout_setting),
|
onClick = {
|
||||||
onClick = { showConfigSheet = true; dismiss() },
|
if (selectedBookUrls.isNotEmpty()) {
|
||||||
leadingIcon = { Icon(Icons.Default.GridView, null) }
|
showGroupSelectSheet = true
|
||||||
)
|
}
|
||||||
RoundDropdownMenuItem(
|
},
|
||||||
text = stringResource(R.string.group_manage),
|
imageVector = Icons.Default.Bookmarks,
|
||||||
onClick = { showGroupManageSheet = true; dismiss() },
|
contentDescription = stringResource(R.string.move_to_group)
|
||||||
leadingIcon = { Icon(Icons.Default.Edit, null) }
|
)
|
||||||
)
|
}
|
||||||
RoundDropdownMenuItem(
|
|
||||||
text = stringResource(R.string.add_url),
|
|
||||||
onClick = { showAddUrlDialog = true; dismiss() },
|
|
||||||
leadingIcon = { Icon(Icons.Default.Link, null) }
|
|
||||||
)
|
|
||||||
RoundDropdownMenuItem(
|
|
||||||
text = stringResource(R.string.bookshelf_management),
|
|
||||||
onClick = {
|
|
||||||
val groupId =
|
|
||||||
uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
|
|
||||||
context.startActivity<BookshelfManageActivity> {
|
|
||||||
putExtra("groupId", groupId)
|
|
||||||
}
|
|
||||||
dismiss()
|
|
||||||
},
|
|
||||||
leadingIcon = { Icon(Icons.Default.Settings, null) }
|
|
||||||
)
|
|
||||||
RoundDropdownMenuItem(
|
|
||||||
text = stringResource(R.string.cache_export),
|
|
||||||
onClick = {
|
|
||||||
val groupId =
|
|
||||||
uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
|
|
||||||
context.startActivity<CacheActivity> {
|
|
||||||
putExtra("groupId", groupId)
|
|
||||||
}
|
|
||||||
dismiss()
|
|
||||||
},
|
|
||||||
leadingIcon = { Icon(Icons.Default.Download, null) }
|
|
||||||
)
|
|
||||||
RoundDropdownMenuItem(
|
|
||||||
text = stringResource(R.string.export_bookshelf),
|
|
||||||
onClick = {
|
|
||||||
showExportSheet = true
|
|
||||||
dismiss()
|
|
||||||
},
|
|
||||||
leadingIcon = { Icon(Icons.Default.ImportExport, null) }
|
|
||||||
)
|
|
||||||
RoundDropdownMenuItem(
|
|
||||||
text = stringResource(R.string.import_bookshelf),
|
|
||||||
onClick = { showImportSheet = true; dismiss() }
|
|
||||||
)
|
|
||||||
RoundDropdownMenuItem(
|
|
||||||
text = stringResource(R.string.log),
|
|
||||||
onClick = {
|
|
||||||
showLogSheet = true
|
|
||||||
dismiss()
|
|
||||||
},
|
|
||||||
leadingIcon = { Icon(Icons.Default.History, null) }
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
|
dropDownMenuContent = if (!isEditMode) {
|
||||||
|
{ dismiss ->
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.add_remote_book),
|
||||||
|
onClick = { onNavigateToRemoteImport(); dismiss() },
|
||||||
|
leadingIcon = { Icon(Icons.Default.Wifi, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.book_local),
|
||||||
|
onClick = { onNavigateToLocalImport(); dismiss() },
|
||||||
|
leadingIcon = { Icon(Icons.Default.Save, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.update_toc),
|
||||||
|
onClick = { viewModel.upToc(uiState.items); dismiss() },
|
||||||
|
leadingIcon = { Icon(Icons.Default.Refresh, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.layout_setting),
|
||||||
|
onClick = { showConfigSheet = true; dismiss() },
|
||||||
|
leadingIcon = { Icon(Icons.Default.GridView, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.group_manage),
|
||||||
|
onClick = { showGroupManageSheet = true; dismiss() },
|
||||||
|
leadingIcon = { Icon(Icons.Default.Edit, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.add_url),
|
||||||
|
onClick = { showAddUrlDialog = true; dismiss() },
|
||||||
|
leadingIcon = { Icon(Icons.Default.Link, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.edit),
|
||||||
|
onClick = {
|
||||||
|
toggleEditMode()
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
leadingIcon = { Icon(Icons.Default.Edit, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.cache_export),
|
||||||
|
onClick = {
|
||||||
|
val groupId =
|
||||||
|
uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
|
||||||
|
context.startActivity<CacheActivity> {
|
||||||
|
putExtra("groupId", groupId)
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
leadingIcon = { Icon(Icons.Default.Download, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.export_bookshelf),
|
||||||
|
onClick = {
|
||||||
|
showExportSheet = true
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
leadingIcon = { Icon(Icons.Default.ImportExport, null) }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.import_bookshelf),
|
||||||
|
onClick = { showImportSheet = true; dismiss() }
|
||||||
|
)
|
||||||
|
RoundDropdownMenuItem(
|
||||||
|
text = stringResource(R.string.log),
|
||||||
|
onClick = {
|
||||||
|
showLogSheet = true
|
||||||
|
dismiss()
|
||||||
|
},
|
||||||
|
leadingIcon = { Icon(Icons.Default.History, null) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
bottomContent = if (bookGroupStyle == 0) {
|
bottomContent = if (bookGroupStyle == 0) {
|
||||||
{
|
{
|
||||||
@@ -388,7 +466,7 @@ fun BookshelfScreen(
|
|||||||
var isRefreshing by remember { mutableStateOf(false) }
|
var isRefreshing by remember { mutableStateOf(false) }
|
||||||
val pullToRefreshState = rememberPullToRefreshState()
|
val pullToRefreshState = rememberPullToRefreshState()
|
||||||
val currentGroup = uiState.groups.getOrNull(pagerState.currentPage)
|
val currentGroup = uiState.groups.getOrNull(pagerState.currentPage)
|
||||||
val pullToRefreshEnabled = currentGroup?.enableRefresh ?: true
|
val pullToRefreshEnabled = (currentGroup?.enableRefresh ?: true) && !isEditMode
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -495,6 +573,9 @@ fun BookshelfScreen(
|
|||||||
bookshelfLayoutMode = bookshelfLayoutMode,
|
bookshelfLayoutMode = bookshelfLayoutMode,
|
||||||
bookshelfLayoutGrid = bookshelfLayoutGrid,
|
bookshelfLayoutGrid = bookshelfLayoutGrid,
|
||||||
bookshelfLayoutList = bookshelfLayoutList,
|
bookshelfLayoutList = bookshelfLayoutList,
|
||||||
|
isEditMode = isEditMode,
|
||||||
|
selectedBookUrls = selectedBookUrls,
|
||||||
|
onToggleBookSelection = { toggleBookSelection(it.bookUrl) },
|
||||||
onBookClick = onBookClick,
|
onBookClick = onBookClick,
|
||||||
onBookLongClick = onBookLongClick
|
onBookLongClick = onBookLongClick
|
||||||
)
|
)
|
||||||
@@ -523,6 +604,17 @@ fun BookshelfScreen(
|
|||||||
onDismissRequest = { showGroupManageSheet = false }
|
onDismissRequest = { showGroupManageSheet = false }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
GroupSelectSheet(
|
||||||
|
show = showGroupSelectSheet,
|
||||||
|
currentGroupId = 0L,
|
||||||
|
onDismissRequest = { showGroupSelectSheet = false },
|
||||||
|
onConfirm = { groupId ->
|
||||||
|
viewModel.moveBooksToGroup(selectedBookUrls, groupId)
|
||||||
|
showGroupSelectSheet = false
|
||||||
|
clearSelection()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
SourceInputDialog(
|
SourceInputDialog(
|
||||||
show = showAddUrlDialog,
|
show = showAddUrlDialog,
|
||||||
title = stringResource(R.string.add_book_url),
|
title = stringResource(R.string.add_book_url),
|
||||||
@@ -599,6 +691,9 @@ fun BookshelfPage(
|
|||||||
bookshelfLayoutMode: Int,
|
bookshelfLayoutMode: Int,
|
||||||
bookshelfLayoutGrid: Int,
|
bookshelfLayoutGrid: Int,
|
||||||
bookshelfLayoutList: Int,
|
bookshelfLayoutList: Int,
|
||||||
|
isEditMode: Boolean,
|
||||||
|
selectedBookUrls: Set<String>,
|
||||||
|
onToggleBookSelection: (BookShelfItem) -> Unit,
|
||||||
onBookClick: (BookShelfItem) -> Unit,
|
onBookClick: (BookShelfItem) -> Unit,
|
||||||
onBookLongClick: (BookShelfItem) -> Unit
|
onBookLongClick: (BookShelfItem) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -636,10 +731,12 @@ fun BookshelfPage(
|
|||||||
showFastScroll = BookshelfConfig.showBookshelfFastScroller
|
showFastScroll = BookshelfConfig.showBookshelfFastScroller
|
||||||
) {
|
) {
|
||||||
items(books, key = { it.bookUrl }) { book ->
|
items(books, key = { it.bookUrl }) { book ->
|
||||||
|
val isSelected = selectedBookUrls.contains(book.bookUrl)
|
||||||
BookItem(
|
BookItem(
|
||||||
book = book,
|
book = book,
|
||||||
modifier = Modifier.animateItem(),
|
modifier = Modifier.animateItem(),
|
||||||
layoutMode = bookshelfLayoutMode,
|
layoutMode = bookshelfLayoutMode,
|
||||||
|
isSelected = isSelected,
|
||||||
gridStyle = BookshelfConfig.bookshelfGridLayout,
|
gridStyle = BookshelfConfig.bookshelfGridLayout,
|
||||||
isCompact = BookshelfConfig.bookshelfLayoutCompact,
|
isCompact = BookshelfConfig.bookshelfLayoutCompact,
|
||||||
isUpdating = uiState.updatingBooks.contains(book.bookUrl),
|
isUpdating = uiState.updatingBooks.contains(book.bookUrl),
|
||||||
@@ -647,10 +744,21 @@ fun BookshelfPage(
|
|||||||
titleCenter = BookshelfConfig.bookshelfTitleCenter,
|
titleCenter = BookshelfConfig.bookshelfTitleCenter,
|
||||||
titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines,
|
titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines,
|
||||||
coverShadow = BookshelfConfig.bookshelfCoverShadow,
|
coverShadow = BookshelfConfig.bookshelfCoverShadow,
|
||||||
onClick = { onBookClick(book) },
|
onClick = {
|
||||||
onLongClick = { onBookLongClick(book) }
|
if (isEditMode) {
|
||||||
|
onToggleBookSelection(book)
|
||||||
|
} else {
|
||||||
|
onBookClick(book)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongClick = {
|
||||||
|
if (isEditMode) {
|
||||||
|
onToggleBookSelection(book)
|
||||||
|
} else {
|
||||||
|
onBookLongClick(book)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -363,6 +363,34 @@ class BookshelfViewModel(
|
|||||||
refreshTrigger.value++
|
refreshTrigger.value++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun moveBooksToGroup(bookUrls: Set<String>, groupId: Long) {
|
||||||
|
updateBooksGroup(bookUrls) { groupId }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateBooksGroup(
|
||||||
|
bookUrls: Set<String>,
|
||||||
|
transform: (Long) -> Long
|
||||||
|
) {
|
||||||
|
if (bookUrls.isEmpty()) return
|
||||||
|
execute {
|
||||||
|
val updateBooks = bookUrls.mapNotNull { url ->
|
||||||
|
appDb.bookDao.getBook(url)?.let { book ->
|
||||||
|
val targetGroup = transform(book.group)
|
||||||
|
if (targetGroup != book.group) {
|
||||||
|
book.copy(group = targetGroup)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updateBooks.isNotEmpty()) {
|
||||||
|
appDb.bookDao.update(*updateBooks.toTypedArray())
|
||||||
|
}
|
||||||
|
}.onError {
|
||||||
|
context.toastOnUi("更新分组失败\n${it.localizedMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun gotoTop() {
|
fun gotoTop() {
|
||||||
scrollTrigger.tryEmit(Unit)
|
scrollTrigger.tryEmit(Unit)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user