🚀 Pin paths in Browse
* Added replacement for directories structure * Always show all files and paths to them * Pin favorite paths Resolves: #91
This commit is contained in:
parent
32cea59c7a
commit
e49f010353
16 changed files with 204 additions and 79 deletions
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ua.acclorite.book_story.domain.browse
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class GroupedFiles(
|
||||||
|
val header: String,
|
||||||
|
val pinned: Boolean,
|
||||||
|
val files: List<SelectableFile>
|
||||||
|
)
|
||||||
|
|
@ -14,6 +14,7 @@ import ua.acclorite.book_story.domain.library.book.SelectableNullableBook
|
||||||
import ua.acclorite.book_story.domain.util.BottomSheet
|
import ua.acclorite.book_story.domain.util.BottomSheet
|
||||||
import ua.acclorite.book_story.domain.util.Dialog
|
import ua.acclorite.book_story.domain.util.Dialog
|
||||||
import ua.acclorite.book_story.ui.browse.BrowseEvent
|
import ua.acclorite.book_story.ui.browse.BrowseEvent
|
||||||
|
import ua.acclorite.book_story.ui.main.MainEvent
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalPermissionsApi::class)
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalPermissionsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -31,6 +32,7 @@ fun BrowseContent(
|
||||||
gridSize: Int,
|
gridSize: Int,
|
||||||
autoGridSize: Boolean,
|
autoGridSize: Boolean,
|
||||||
includedFilterItems: List<String>,
|
includedFilterItems: List<String>,
|
||||||
|
pinnedPaths: List<String>,
|
||||||
canScrollBackList: Boolean,
|
canScrollBackList: Boolean,
|
||||||
canScrollBackGrid: Boolean,
|
canScrollBackGrid: Boolean,
|
||||||
hasSelectedItems: Boolean,
|
hasSelectedItems: Boolean,
|
||||||
|
|
@ -59,6 +61,7 @@ fun BrowseContent(
|
||||||
dismissAddDialog: (BrowseEvent.OnDismissAddDialog) -> Unit,
|
dismissAddDialog: (BrowseEvent.OnDismissAddDialog) -> Unit,
|
||||||
actionAddDialog: (BrowseEvent.OnActionAddDialog) -> Unit,
|
actionAddDialog: (BrowseEvent.OnActionAddDialog) -> Unit,
|
||||||
selectAddDialog: (BrowseEvent.OnSelectAddDialog) -> Unit,
|
selectAddDialog: (BrowseEvent.OnSelectAddDialog) -> Unit,
|
||||||
|
changePinnedPaths: (MainEvent.OnChangeBrowsePinnedPaths) -> Unit,
|
||||||
navigateToLibrary: () -> Unit,
|
navigateToLibrary: () -> Unit,
|
||||||
navigateToHelp: () -> Unit,
|
navigateToHelp: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
@ -89,6 +92,7 @@ fun BrowseContent(
|
||||||
gridSize = gridSize,
|
gridSize = gridSize,
|
||||||
autoGridSize = autoGridSize,
|
autoGridSize = autoGridSize,
|
||||||
includedFilterItems = includedFilterItems,
|
includedFilterItems = includedFilterItems,
|
||||||
|
pinnedPaths = pinnedPaths,
|
||||||
canScrollBackList = canScrollBackList,
|
canScrollBackList = canScrollBackList,
|
||||||
canScrollBackGrid = canScrollBackGrid,
|
canScrollBackGrid = canScrollBackGrid,
|
||||||
hasSelectedItems = hasSelectedItems,
|
hasSelectedItems = hasSelectedItems,
|
||||||
|
|
@ -112,6 +116,7 @@ fun BrowseContent(
|
||||||
selectFile = selectFile,
|
selectFile = selectFile,
|
||||||
showFilterBottomSheet = showFilterBottomSheet,
|
showFilterBottomSheet = showFilterBottomSheet,
|
||||||
showAddDialog = showAddDialog,
|
showAddDialog = showAddDialog,
|
||||||
|
changePinnedPaths = changePinnedPaths,
|
||||||
navigateToHelp = navigateToHelp
|
navigateToHelp = navigateToHelp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package ua.acclorite.book_story.presentation.browse
|
package ua.acclorite.book_story.presentation.browse
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
|
@ -10,7 +11,7 @@ import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ua.acclorite.book_story.domain.browse.BrowseLayout
|
import ua.acclorite.book_story.domain.browse.GroupedFiles
|
||||||
import ua.acclorite.book_story.domain.browse.SelectableFile
|
import ua.acclorite.book_story.domain.browse.SelectableFile
|
||||||
import ua.acclorite.book_story.presentation.core.components.common.LazyVerticalGridWithScrollbar
|
import ua.acclorite.book_story.presentation.core.components.common.LazyVerticalGridWithScrollbar
|
||||||
import ua.acclorite.book_story.presentation.core.components.common.header
|
import ua.acclorite.book_story.presentation.core.components.common.header
|
||||||
|
|
@ -19,43 +20,34 @@ import ua.acclorite.book_story.presentation.core.constants.providePrimaryScrollb
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BrowseGridLayout(
|
fun BrowseGridLayout(
|
||||||
|
groupedFiles: List<GroupedFiles>,
|
||||||
gridSize: Int,
|
gridSize: Int,
|
||||||
autoGridSize: Boolean,
|
autoGridSize: Boolean,
|
||||||
gridState: LazyGridState,
|
gridState: LazyGridState,
|
||||||
files: List<SelectableFile>,
|
headerContent: @Composable (header: String, pinned: Boolean) -> Unit,
|
||||||
hasSelectedItems: Boolean,
|
itemContent: @Composable (file: SelectableFile, files: List<SelectableFile>) -> Unit
|
||||||
onLongItemClick: (SelectableFile) -> Unit,
|
|
||||||
onItemClick: (SelectableFile) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
LazyVerticalGridWithScrollbar(
|
LazyVerticalGridWithScrollbar(
|
||||||
columns = if (autoGridSize) GridCells.Adaptive(170.dp)
|
columns = if (autoGridSize) GridCells.Adaptive(170.dp)
|
||||||
else GridCells.Fixed(gridSize.coerceAtLeast(1)),
|
else GridCells.Fixed(gridSize.coerceAtLeast(1)),
|
||||||
state = gridState,
|
state = gridState,
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize(),
|
||||||
.fillMaxSize(),
|
|
||||||
contentPadding = PaddingValues(horizontal = 8.dp),
|
contentPadding = PaddingValues(horizontal = 8.dp),
|
||||||
scrollbarSettings = Constants.providePrimaryScrollbar(false)
|
scrollbarSettings = Constants.providePrimaryScrollbar(false)
|
||||||
) {
|
) {
|
||||||
header {
|
groupedFiles.forEach { group ->
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
stickyHeader {
|
||||||
|
headerContent(group.header, group.pinned)
|
||||||
}
|
}
|
||||||
|
|
||||||
items(
|
items(
|
||||||
files,
|
group.files,
|
||||||
key = { it.path }
|
key = { it.path }
|
||||||
) { selectableFile ->
|
) { selectableFile ->
|
||||||
BrowseItem(
|
Box(Modifier.animateItem()) {
|
||||||
file = selectableFile,
|
itemContent(selectableFile, group.files)
|
||||||
modifier = Modifier.animateItem(),
|
}
|
||||||
layout = BrowseLayout.GRID,
|
|
||||||
hasSelectedItems = hasSelectedItems,
|
|
||||||
onLongClick = {
|
|
||||||
onLongItemClick(selectableFile)
|
|
||||||
},
|
|
||||||
onClick = {
|
|
||||||
onItemClick(selectableFile)
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ import ua.acclorite.book_story.domain.browse.SelectableFile
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BrowseItem(
|
fun BrowseItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
layout: BrowseLayout,
|
layout: BrowseLayout,
|
||||||
file: SelectableFile,
|
file: SelectableFile,
|
||||||
hasSelectedItems: Boolean,
|
hasSelectedItems: Boolean,
|
||||||
modifier: Modifier,
|
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit
|
onLongClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -3,41 +3,56 @@ package ua.acclorite.book_story.presentation.browse
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.grid.LazyGridState
|
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import ua.acclorite.book_story.domain.browse.BrowseLayout
|
import ua.acclorite.book_story.domain.browse.BrowseLayout
|
||||||
|
import ua.acclorite.book_story.domain.browse.GroupedFiles
|
||||||
import ua.acclorite.book_story.domain.browse.SelectableFile
|
import ua.acclorite.book_story.domain.browse.SelectableFile
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BrowseLayout(
|
fun BrowseLayout(
|
||||||
files: List<SelectableFile>,
|
files: List<SelectableFile>,
|
||||||
hasSelectedItems: Boolean,
|
pinnedPaths: List<String>,
|
||||||
layout: BrowseLayout,
|
layout: BrowseLayout,
|
||||||
gridSize: Int,
|
gridSize: Int,
|
||||||
autoGridSize: Boolean,
|
autoGridSize: Boolean,
|
||||||
listState: LazyListState,
|
listState: LazyListState,
|
||||||
gridState: LazyGridState,
|
gridState: LazyGridState,
|
||||||
onLongItemClick: (SelectableFile) -> Unit,
|
headerContent: @Composable (header: String, pinned: Boolean) -> Unit,
|
||||||
onItemClick: (SelectableFile) -> Unit
|
itemContent: @Composable (file: SelectableFile, files: List<SelectableFile>) -> Unit
|
||||||
) {
|
) {
|
||||||
|
val groupedFiles = remember(files, pinnedPaths) {
|
||||||
|
files.groupBy { file ->
|
||||||
|
file.path.substringBeforeLast(File.separator)
|
||||||
|
}.map { (header, files) ->
|
||||||
|
GroupedFiles(
|
||||||
|
header = header,
|
||||||
|
pinned = pinnedPaths.any { it.lowercase().trim() == header.lowercase().trim() },
|
||||||
|
files = files
|
||||||
|
)
|
||||||
|
}.sortedByDescending { group ->
|
||||||
|
group.pinned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when (layout) {
|
when (layout) {
|
||||||
BrowseLayout.LIST -> {
|
BrowseLayout.LIST -> {
|
||||||
BrowseListLayout(
|
BrowseListLayout(
|
||||||
files = files,
|
groupedFiles = groupedFiles,
|
||||||
hasSelectedItems = hasSelectedItems,
|
|
||||||
listState = listState,
|
listState = listState,
|
||||||
onLongItemClick = onLongItemClick,
|
headerContent = headerContent,
|
||||||
onItemClick = onItemClick
|
itemContent = itemContent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowseLayout.GRID -> {
|
BrowseLayout.GRID -> {
|
||||||
BrowseGridLayout(
|
BrowseGridLayout(
|
||||||
|
groupedFiles = groupedFiles,
|
||||||
gridSize = gridSize,
|
gridSize = gridSize,
|
||||||
autoGridSize = autoGridSize,
|
autoGridSize = autoGridSize,
|
||||||
files = files,
|
|
||||||
hasSelectedItems = hasSelectedItems,
|
|
||||||
gridState = gridState,
|
gridState = gridState,
|
||||||
onLongItemClick = onLongItemClick,
|
headerContent = headerContent,
|
||||||
onItemClick = onItemClick
|
itemContent = itemContent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package ua.acclorite.book_story.presentation.browse
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.PushPin
|
||||||
|
import androidx.compose.material.icons.outlined.PushPin
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.presentation.core.util.noRippleClickable
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BrowseLayoutHeader(
|
||||||
|
header: String,
|
||||||
|
pinned: Boolean,
|
||||||
|
pin: () -> Unit
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
text = header,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
style = MaterialTheme.typography.bodyLarge
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(20.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
pin()
|
||||||
|
},
|
||||||
|
imageVector = if (pinned) Icons.Default.PushPin
|
||||||
|
else Icons.Outlined.PushPin,
|
||||||
|
contentDescription = stringResource(R.string.pin_content_desc),
|
||||||
|
tint = if (pinned) MaterialTheme.colorScheme.primary
|
||||||
|
else MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ fun BrowseListItem(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 8.dp, vertical = 3.dp)
|
.padding(vertical = 3.dp)
|
||||||
.clip(RoundedCornerShape(10.dp))
|
.clip(RoundedCornerShape(10.dp))
|
||||||
.background(
|
.background(
|
||||||
if (file.selected) MaterialTheme.colorScheme.secondaryContainer
|
if (file.selected) MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package ua.acclorite.book_story.presentation.browse
|
package ua.acclorite.book_story.presentation.browse
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
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.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
|
@ -8,7 +10,7 @@ import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ua.acclorite.book_story.domain.browse.BrowseLayout
|
import ua.acclorite.book_story.domain.browse.GroupedFiles
|
||||||
import ua.acclorite.book_story.domain.browse.SelectableFile
|
import ua.acclorite.book_story.domain.browse.SelectableFile
|
||||||
import ua.acclorite.book_story.presentation.core.components.common.LazyColumnWithScrollbar
|
import ua.acclorite.book_story.presentation.core.components.common.LazyColumnWithScrollbar
|
||||||
import ua.acclorite.book_story.presentation.core.constants.Constants
|
import ua.acclorite.book_story.presentation.core.constants.Constants
|
||||||
|
|
@ -16,37 +18,30 @@ import ua.acclorite.book_story.presentation.core.constants.providePrimaryScrollb
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BrowseListLayout(
|
fun BrowseListLayout(
|
||||||
files: List<SelectableFile>,
|
groupedFiles: List<GroupedFiles>,
|
||||||
hasSelectedItems: Boolean,
|
|
||||||
listState: LazyListState,
|
listState: LazyListState,
|
||||||
onLongItemClick: (SelectableFile) -> Unit,
|
headerContent: @Composable (header: String, pinned: Boolean) -> Unit,
|
||||||
onItemClick: (SelectableFile) -> Unit,
|
itemContent: @Composable (file: SelectableFile, files: List<SelectableFile>) -> Unit
|
||||||
) {
|
) {
|
||||||
LazyColumnWithScrollbar(
|
LazyColumnWithScrollbar(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentPadding = PaddingValues(horizontal = 8.dp),
|
||||||
scrollbarSettings = Constants.providePrimaryScrollbar(false)
|
scrollbarSettings = Constants.providePrimaryScrollbar(false)
|
||||||
) {
|
) {
|
||||||
item {
|
groupedFiles.forEach { group ->
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
stickyHeader {
|
||||||
|
headerContent(group.header, group.pinned)
|
||||||
}
|
}
|
||||||
|
|
||||||
items(
|
items(
|
||||||
files,
|
group.files,
|
||||||
key = { it.path }
|
key = { it.path }
|
||||||
) { selectableFile ->
|
) { selectableFile ->
|
||||||
BrowseItem(
|
Box(Modifier.animateItem()) {
|
||||||
file = selectableFile,
|
itemContent(selectableFile, group.files)
|
||||||
layout = BrowseLayout.LIST,
|
}
|
||||||
modifier = Modifier.animateItem(),
|
|
||||||
hasSelectedItems = hasSelectedItems,
|
|
||||||
onLongClick = {
|
|
||||||
onLongItemClick(selectableFile)
|
|
||||||
},
|
|
||||||
onClick = {
|
|
||||||
onItemClick(selectableFile)
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,10 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||||
import com.google.accompanist.permissions.PermissionState
|
import com.google.accompanist.permissions.PermissionState
|
||||||
import ua.acclorite.book_story.R
|
|
||||||
import ua.acclorite.book_story.domain.browse.BrowseLayout
|
import ua.acclorite.book_story.domain.browse.BrowseLayout
|
||||||
import ua.acclorite.book_story.domain.browse.SelectableFile
|
import ua.acclorite.book_story.domain.browse.SelectableFile
|
||||||
import ua.acclorite.book_story.presentation.core.util.LocalActivity
|
|
||||||
import ua.acclorite.book_story.presentation.core.util.showToast
|
|
||||||
import ua.acclorite.book_story.ui.browse.BrowseEvent
|
import ua.acclorite.book_story.ui.browse.BrowseEvent
|
||||||
|
import ua.acclorite.book_story.ui.main.MainEvent
|
||||||
import ua.acclorite.book_story.ui.theme.DefaultTransition
|
import ua.acclorite.book_story.ui.theme.DefaultTransition
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalPermissionsApi::class)
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalPermissionsApi::class)
|
||||||
|
|
@ -35,6 +33,7 @@ fun BrowseScaffold(
|
||||||
gridSize: Int,
|
gridSize: Int,
|
||||||
autoGridSize: Boolean,
|
autoGridSize: Boolean,
|
||||||
includedFilterItems: List<String>,
|
includedFilterItems: List<String>,
|
||||||
|
pinnedPaths: List<String>,
|
||||||
canScrollBackList: Boolean,
|
canScrollBackList: Boolean,
|
||||||
canScrollBackGrid: Boolean,
|
canScrollBackGrid: Boolean,
|
||||||
hasSelectedItems: Boolean,
|
hasSelectedItems: Boolean,
|
||||||
|
|
@ -57,10 +56,9 @@ fun BrowseScaffold(
|
||||||
permissionCheck: (BrowseEvent.OnPermissionCheck) -> Unit,
|
permissionCheck: (BrowseEvent.OnPermissionCheck) -> Unit,
|
||||||
showFilterBottomSheet: (BrowseEvent.OnShowFilterBottomSheet) -> Unit,
|
showFilterBottomSheet: (BrowseEvent.OnShowFilterBottomSheet) -> Unit,
|
||||||
showAddDialog: (BrowseEvent.OnShowAddDialog) -> Unit,
|
showAddDialog: (BrowseEvent.OnShowAddDialog) -> Unit,
|
||||||
|
changePinnedPaths: (MainEvent.OnChangeBrowsePinnedPaths) -> Unit,
|
||||||
navigateToHelp: () -> Unit,
|
navigateToHelp: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalActivity.current
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -97,19 +95,39 @@ fun BrowseScaffold(
|
||||||
DefaultTransition(visible = !isLoading) {
|
DefaultTransition(visible = !isLoading) {
|
||||||
BrowseLayout(
|
BrowseLayout(
|
||||||
files = files,
|
files = files,
|
||||||
|
pinnedPaths = pinnedPaths,
|
||||||
layout = layout,
|
layout = layout,
|
||||||
hasSelectedItems = hasSelectedItems,
|
|
||||||
gridSize = gridSize,
|
gridSize = gridSize,
|
||||||
autoGridSize = autoGridSize,
|
autoGridSize = autoGridSize,
|
||||||
listState = listState,
|
listState = listState,
|
||||||
gridState = gridState,
|
gridState = gridState,
|
||||||
onLongItemClick = { file ->
|
headerContent = { header, pinned ->
|
||||||
context.getString(
|
BrowseLayoutHeader(
|
||||||
R.string.file_path_query,
|
header = header,
|
||||||
file.path
|
pinned = pinned,
|
||||||
).showToast(context = context)
|
pin = {
|
||||||
|
changePinnedPaths(
|
||||||
|
MainEvent.OnChangeBrowsePinnedPaths(
|
||||||
|
value = header
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onItemClick = { file ->
|
itemContent = { file, groupFiles ->
|
||||||
|
BrowseItem(
|
||||||
|
file = file,
|
||||||
|
layout = layout,
|
||||||
|
hasSelectedItems = hasSelectedItems,
|
||||||
|
onLongClick = {
|
||||||
|
selectFiles(
|
||||||
|
BrowseEvent.OnSelectFiles(
|
||||||
|
includedFileFormats = includedFilterItems,
|
||||||
|
files = groupFiles
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
selectFile(
|
selectFile(
|
||||||
BrowseEvent.OnSelectFile(
|
BrowseEvent.OnSelectFile(
|
||||||
includedFileFormats = includedFilterItems,
|
includedFileFormats = includedFilterItems,
|
||||||
|
|
@ -119,6 +137,8 @@ fun BrowseScaffold(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
BrowseEmptyPlaceholder(
|
BrowseEmptyPlaceholder(
|
||||||
filesEmpty = filesEmpty,
|
filesEmpty = filesEmpty,
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,5 @@ object DataStoreConstants {
|
||||||
val BROWSE_SORT_ORDER = stringPreferencesKey("browse_sort_order")
|
val BROWSE_SORT_ORDER = stringPreferencesKey("browse_sort_order")
|
||||||
val BROWSE_SORT_ORDER_DESCENDING = booleanPreferencesKey("browse_sort_order_descending")
|
val BROWSE_SORT_ORDER_DESCENDING = booleanPreferencesKey("browse_sort_order_descending")
|
||||||
val BROWSE_INCLUDED_FILTER_ITEMS = stringSetPreferencesKey("browse_included_filter_items")
|
val BROWSE_INCLUDED_FILTER_ITEMS = stringSetPreferencesKey("browse_included_filter_items")
|
||||||
|
val BROWSE_PINNED_PATHS = stringSetPreferencesKey("browse_pinned_paths")
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +153,7 @@ object BrowseScreen : Screen, Parcelable {
|
||||||
gridSize = mainState.value.browseGridSize,
|
gridSize = mainState.value.browseGridSize,
|
||||||
autoGridSize = mainState.value.browseAutoGridSize,
|
autoGridSize = mainState.value.browseAutoGridSize,
|
||||||
includedFilterItems = mainState.value.browseIncludedFilterItems,
|
includedFilterItems = mainState.value.browseIncludedFilterItems,
|
||||||
|
pinnedPaths = mainState.value.browsePinnedPaths,
|
||||||
canScrollBackList = listState.canScrollBackward,
|
canScrollBackList = listState.canScrollBackward,
|
||||||
canScrollBackGrid = gridState.canScrollBackward,
|
canScrollBackGrid = gridState.canScrollBackward,
|
||||||
hasSelectedItems = state.value.hasSelectedItems,
|
hasSelectedItems = state.value.hasSelectedItems,
|
||||||
|
|
@ -181,6 +182,7 @@ object BrowseScreen : Screen, Parcelable {
|
||||||
dismissAddDialog = screenModel::onEvent,
|
dismissAddDialog = screenModel::onEvent,
|
||||||
selectAddDialog = screenModel::onEvent,
|
selectAddDialog = screenModel::onEvent,
|
||||||
actionAddDialog = screenModel::onEvent,
|
actionAddDialog = screenModel::onEvent,
|
||||||
|
changePinnedPaths = mainModel::onEvent,
|
||||||
navigateToLibrary = {
|
navigateToLibrary = {
|
||||||
navigator.push(LibraryScreen, saveInBackStack = false)
|
navigator.push(LibraryScreen, saveInBackStack = false)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -57,4 +57,5 @@ sealed class MainEvent {
|
||||||
data class OnChangeProgressBarPadding(val value: Int) : MainEvent()
|
data class OnChangeProgressBarPadding(val value: Int) : MainEvent()
|
||||||
data class OnChangeProgressBarAlignment(val value: String) : MainEvent()
|
data class OnChangeProgressBarAlignment(val value: String) : MainEvent()
|
||||||
data class OnChangeProgressBarFontSize(val value: Int) : MainEvent()
|
data class OnChangeProgressBarFontSize(val value: Int) : MainEvent()
|
||||||
|
data class OnChangeBrowsePinnedPaths(val value: String) : MainEvent()
|
||||||
}
|
}
|
||||||
|
|
@ -229,7 +229,7 @@ class MainModel @Inject constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
is MainEvent.OnChangeBrowseIncludedFilterItem -> handleBrowseIncludedFilterItemUpdate(
|
is MainEvent.OnChangeBrowseIncludedFilterItem -> handleBrowseIncludedFilterItemUpdate(
|
||||||
event
|
event = event
|
||||||
)
|
)
|
||||||
|
|
||||||
is MainEvent.OnChangeTextAlignment -> handleDatastoreUpdate(
|
is MainEvent.OnChangeTextAlignment -> handleDatastoreUpdate(
|
||||||
|
|
@ -479,6 +479,10 @@ class MainModel @Inject constructor(
|
||||||
it.copy(progressBarFontSize = this)
|
it.copy(progressBarFontSize = this)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is MainEvent.OnChangeBrowsePinnedPaths -> handleBrowsePinnedPathsUpdate(
|
||||||
|
event = event
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -546,6 +550,22 @@ class MainModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleBrowsePinnedPathsUpdate(
|
||||||
|
event: MainEvent.OnChangeBrowsePinnedPaths
|
||||||
|
) {
|
||||||
|
val set = _state.value.browsePinnedPaths.toMutableSet()
|
||||||
|
if (!set.add(event.value)) {
|
||||||
|
set.remove(event.value)
|
||||||
|
}
|
||||||
|
handleDatastoreUpdate(
|
||||||
|
key = DataStoreConstants.BROWSE_PINNED_PATHS,
|
||||||
|
value = set,
|
||||||
|
updateState = {
|
||||||
|
it.copy(browsePinnedPaths = toList())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles and updates Datastore.
|
* Handles and updates Datastore.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ data class MainState(
|
||||||
val browseSortOrder: BrowseSortOrder = provideDefaultValue { BrowseSortOrder.LAST_MODIFIED },
|
val browseSortOrder: BrowseSortOrder = provideDefaultValue { BrowseSortOrder.LAST_MODIFIED },
|
||||||
val browseSortOrderDescending: Boolean = provideDefaultValue { true },
|
val browseSortOrderDescending: Boolean = provideDefaultValue { true },
|
||||||
val browseIncludedFilterItems: List<String> = provideDefaultValue { emptyList() },
|
val browseIncludedFilterItems: List<String> = provideDefaultValue { emptyList() },
|
||||||
|
val browsePinnedPaths: List<String> = provideDefaultValue { emptyList() },
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
companion object {
|
companion object {
|
||||||
private fun <D> provideDefaultValue(calculation: () -> D): D {
|
private fun <D> provideDefaultValue(calculation: () -> D): D {
|
||||||
|
|
@ -345,6 +346,10 @@ data class MainState(
|
||||||
progressBarFontSize = provideValue(
|
progressBarFontSize = provideValue(
|
||||||
PROGRESS_BAR_FONT_SIZE
|
PROGRESS_BAR_FONT_SIZE
|
||||||
) { progressBarFontSize },
|
) { progressBarFontSize },
|
||||||
|
|
||||||
|
browsePinnedPaths = provideValue(
|
||||||
|
BROWSE_PINNED_PATHS, convert = { toList() }
|
||||||
|
) { browsePinnedPaths },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -463,4 +463,6 @@
|
||||||
<string name="checkpoint_forward_content_desc">Чекпоінт вперед</string>
|
<string name="checkpoint_forward_content_desc">Чекпоінт вперед</string>
|
||||||
<string name="chapters_content_desc">Розділи</string>
|
<string name="chapters_content_desc">Розділи</string>
|
||||||
<string name="more_content_desc">Більше</string>
|
<string name="more_content_desc">Більше</string>
|
||||||
|
<string name="pin_content_desc">Закріпити</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -623,5 +623,6 @@
|
||||||
<string name="checkpoint_forward_content_desc">Checkpoint forward</string>
|
<string name="checkpoint_forward_content_desc">Checkpoint forward</string>
|
||||||
<string name="chapters_content_desc">Chapters</string>
|
<string name="chapters_content_desc">Chapters</string>
|
||||||
<string name="more_content_desc">More</string>
|
<string name="more_content_desc">More</string>
|
||||||
|
<string name="pin_content_desc">Pin</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue