Proper two-pane layout for Duplicates screen
This commit is contained in:
parent
96efcfad65
commit
42dab0318b
|
@ -5,8 +5,12 @@ import com.artemchep.keyguard.common.model.DFilter
|
|||
import com.artemchep.keyguard.feature.navigation.Route
|
||||
|
||||
data class DuplicatesRoute(
|
||||
val args: Args = Args(),
|
||||
val args: Args,
|
||||
) : Route {
|
||||
companion object {
|
||||
const val ROUTER_NAME = "duplicates"
|
||||
}
|
||||
|
||||
data class Args(
|
||||
val filter: DFilter? = null,
|
||||
)
|
||||
|
|
|
@ -1,193 +1,24 @@
|
|||
package com.artemchep.keyguard.feature.duplicates
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
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.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.artemchep.keyguard.common.model.fold
|
||||
import com.artemchep.keyguard.common.model.getOrNull
|
||||
import com.artemchep.keyguard.feature.EmptySearchView
|
||||
import com.artemchep.keyguard.feature.home.vault.component.VaultListItem
|
||||
import com.artemchep.keyguard.feature.navigation.NavigationIcon
|
||||
import com.artemchep.keyguard.res.Res
|
||||
import com.artemchep.keyguard.ui.DefaultSelection
|
||||
import com.artemchep.keyguard.ui.DropdownMenuItemFlat
|
||||
import com.artemchep.keyguard.ui.DropdownMinWidth
|
||||
import com.artemchep.keyguard.ui.DropdownScopeImpl
|
||||
import com.artemchep.keyguard.ui.ExpandedIfNotEmpty
|
||||
import com.artemchep.keyguard.ui.MediumEmphasisAlpha
|
||||
import com.artemchep.keyguard.ui.ScaffoldLazyColumn
|
||||
import com.artemchep.keyguard.ui.icons.DropdownIcon
|
||||
import com.artemchep.keyguard.ui.skeleton.SkeletonItem
|
||||
import com.artemchep.keyguard.ui.theme.Dimens
|
||||
import com.artemchep.keyguard.ui.theme.combineAlpha
|
||||
import com.artemchep.keyguard.ui.toolbar.LargeToolbar
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import com.artemchep.keyguard.feature.duplicates.list.DuplicatesListRoute
|
||||
import com.artemchep.keyguard.feature.navigation.NavigationRouter
|
||||
import com.artemchep.keyguard.feature.twopane.TwoPaneNavigationContent
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class,
|
||||
ExperimentalMaterialApi::class,
|
||||
ExperimentalFoundationApi::class,
|
||||
)
|
||||
@Composable
|
||||
fun DuplicatesScreen(
|
||||
args: DuplicatesRoute.Args,
|
||||
) {
|
||||
val loadableState = produceDuplicatesState(args)
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
ScaffoldLazyColumn(
|
||||
modifier = Modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topAppBarScrollBehavior = scrollBehavior,
|
||||
topBar = {
|
||||
LargeToolbar(
|
||||
title = {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(Res.strings.watchtower_header_title),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = LocalContentColor.current
|
||||
.combineAlpha(MediumEmphasisAlpha),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 2,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(Res.strings.watchtower_item_duplicate_items_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
NavigationIcon()
|
||||
},
|
||||
actions = {
|
||||
var isAutofillWindowShowing by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val normalContentColor = LocalContentColor.current
|
||||
TextButton(
|
||||
onClick = {
|
||||
isAutofillWindowShowing = true
|
||||
},
|
||||
) {
|
||||
Column {
|
||||
Text(text = "Sensitivity")
|
||||
val textOrNull =
|
||||
loadableState.getOrNull()?.sensitivity?.name
|
||||
?.lowercase()
|
||||
?.capitalize()
|
||||
ExpandedIfNotEmpty(
|
||||
valueOrNull = textOrNull,
|
||||
) { text ->
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = normalContentColor
|
||||
.combineAlpha(MediumEmphasisAlpha),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(Dimens.buttonIconPadding),
|
||||
)
|
||||
DropdownIcon()
|
||||
|
||||
// Inject the dropdown popup to the bottom of the
|
||||
// content.
|
||||
val onDismissRequest = {
|
||||
isAutofillWindowShowing = false
|
||||
}
|
||||
DropdownMenu(
|
||||
modifier = Modifier
|
||||
.widthIn(min = DropdownMinWidth),
|
||||
expanded = isAutofillWindowShowing,
|
||||
onDismissRequest = onDismissRequest,
|
||||
) {
|
||||
val scope = DropdownScopeImpl(this, onDismissRequest = onDismissRequest)
|
||||
loadableState.getOrNull()?.sensitivities.orEmpty()
|
||||
.forEachIndexed { index, action ->
|
||||
scope.DropdownMenuItemFlat(
|
||||
action = action,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
val screenState = loadableState.getOrNull()
|
||||
?: return@ScaffoldLazyColumn
|
||||
val selectionState = screenState.selectionStateFlow.collectAsState()
|
||||
DefaultSelection(
|
||||
state = selectionState.value,
|
||||
)
|
||||
},
|
||||
) {
|
||||
loadableState.fold(
|
||||
ifLoading = {
|
||||
for (i in 0..2) {
|
||||
item(i) {
|
||||
SkeletonItem()
|
||||
}
|
||||
}
|
||||
},
|
||||
ifOk = { state ->
|
||||
val items = state.items
|
||||
if (items.isEmpty()) {
|
||||
item("header.empty") {
|
||||
NoItemsPlaceholder()
|
||||
}
|
||||
}
|
||||
items(
|
||||
items = items,
|
||||
key = { model -> model.id },
|
||||
) { model ->
|
||||
VaultListItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement(),
|
||||
item = model,
|
||||
)
|
||||
}
|
||||
},
|
||||
val initialRoute = remember(args) {
|
||||
DuplicatesListRoute(
|
||||
args = args,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NoItemsPlaceholder(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
EmptySearchView(
|
||||
modifier = modifier,
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(Res.strings.duplicates_empty_label),
|
||||
)
|
||||
},
|
||||
)
|
||||
NavigationRouter(
|
||||
id = DuplicatesRoute.ROUTER_NAME,
|
||||
initial = initialRoute,
|
||||
) { backStack ->
|
||||
TwoPaneNavigationContent(backStack)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package com.artemchep.keyguard.feature.duplicates.list
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.artemchep.keyguard.feature.duplicates.DuplicatesRoute
|
||||
import com.artemchep.keyguard.feature.navigation.Route
|
||||
|
||||
data class DuplicatesListRoute(
|
||||
val args: DuplicatesRoute.Args,
|
||||
) : Route {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
DuplicatesListScreen(args)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
package com.artemchep.keyguard.feature.duplicates.list
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.artemchep.keyguard.common.model.fold
|
||||
import com.artemchep.keyguard.common.model.getOrNull
|
||||
import com.artemchep.keyguard.feature.EmptySearchView
|
||||
import com.artemchep.keyguard.feature.duplicates.DuplicatesRoute
|
||||
import com.artemchep.keyguard.feature.home.vault.component.VaultListItem
|
||||
import com.artemchep.keyguard.feature.home.vault.screen.VaultViewRoute
|
||||
import com.artemchep.keyguard.feature.navigation.LocalNavigationEntry
|
||||
import com.artemchep.keyguard.feature.navigation.LocalNavigationRouter
|
||||
import com.artemchep.keyguard.feature.navigation.NavigationIcon
|
||||
import com.artemchep.keyguard.res.Res
|
||||
import com.artemchep.keyguard.ui.Compose
|
||||
import com.artemchep.keyguard.ui.DefaultSelection
|
||||
import com.artemchep.keyguard.ui.DropdownMenuItemFlat
|
||||
import com.artemchep.keyguard.ui.DropdownMinWidth
|
||||
import com.artemchep.keyguard.ui.DropdownScopeImpl
|
||||
import com.artemchep.keyguard.ui.ExpandedIfNotEmpty
|
||||
import com.artemchep.keyguard.ui.MediumEmphasisAlpha
|
||||
import com.artemchep.keyguard.ui.ScaffoldLazyColumn
|
||||
import com.artemchep.keyguard.ui.icons.DropdownIcon
|
||||
import com.artemchep.keyguard.ui.skeleton.SkeletonItem
|
||||
import com.artemchep.keyguard.ui.theme.Dimens
|
||||
import com.artemchep.keyguard.ui.theme.combineAlpha
|
||||
import com.artemchep.keyguard.ui.toolbar.LargeToolbar
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class,
|
||||
ExperimentalMaterialApi::class,
|
||||
ExperimentalFoundationApi::class,
|
||||
)
|
||||
@Composable
|
||||
fun DuplicatesListScreen(
|
||||
args: DuplicatesRoute.Args,
|
||||
) {
|
||||
val loadableState = produceDuplicatesListState(args)
|
||||
|
||||
val onSelected = loadableState.getOrNull()?.onSelected
|
||||
Compose {
|
||||
val screenId = LocalNavigationEntry.current.id
|
||||
val screenStack = LocalNavigationRouter.current.value
|
||||
val childBackStackFlow = remember(
|
||||
screenId,
|
||||
screenStack,
|
||||
) {
|
||||
snapshotFlow {
|
||||
val backStack = screenStack
|
||||
.indexOfFirst { it.id == screenId }
|
||||
// take the next screen
|
||||
.inc()
|
||||
// check if in range
|
||||
.takeIf { it in 1 until screenStack.size }
|
||||
?.let { index ->
|
||||
screenStack.subList(
|
||||
fromIndex = index,
|
||||
toIndex = screenStack.size,
|
||||
)
|
||||
}
|
||||
.orEmpty()
|
||||
backStack
|
||||
}
|
||||
}
|
||||
LaunchedEffect(onSelected, childBackStackFlow) {
|
||||
childBackStackFlow.collect { backStack ->
|
||||
val firstRoute = backStack.firstOrNull()?.route as? VaultViewRoute?
|
||||
onSelected?.invoke(firstRoute?.itemId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
ScaffoldLazyColumn(
|
||||
modifier = Modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topAppBarScrollBehavior = scrollBehavior,
|
||||
topBar = {
|
||||
LargeToolbar(
|
||||
title = {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(Res.strings.watchtower_header_title),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = LocalContentColor.current
|
||||
.combineAlpha(MediumEmphasisAlpha),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 2,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(Res.strings.watchtower_item_duplicate_items_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
NavigationIcon()
|
||||
},
|
||||
actions = {
|
||||
var isAutofillWindowShowing by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val normalContentColor = LocalContentColor.current
|
||||
TextButton(
|
||||
onClick = {
|
||||
isAutofillWindowShowing = true
|
||||
},
|
||||
) {
|
||||
Column {
|
||||
Text(text = "Sensitivity")
|
||||
val textOrNull =
|
||||
loadableState.getOrNull()?.sensitivity?.name
|
||||
?.lowercase()
|
||||
?.capitalize()
|
||||
ExpandedIfNotEmpty(
|
||||
valueOrNull = textOrNull,
|
||||
) { text ->
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = normalContentColor
|
||||
.combineAlpha(MediumEmphasisAlpha),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.width(Dimens.buttonIconPadding),
|
||||
)
|
||||
DropdownIcon()
|
||||
|
||||
// Inject the dropdown popup to the bottom of the
|
||||
// content.
|
||||
val onDismissRequest = {
|
||||
isAutofillWindowShowing = false
|
||||
}
|
||||
DropdownMenu(
|
||||
modifier = Modifier
|
||||
.widthIn(min = DropdownMinWidth),
|
||||
expanded = isAutofillWindowShowing,
|
||||
onDismissRequest = onDismissRequest,
|
||||
) {
|
||||
val scope = DropdownScopeImpl(this, onDismissRequest = onDismissRequest)
|
||||
loadableState.getOrNull()?.sensitivities.orEmpty()
|
||||
.forEachIndexed { index, action ->
|
||||
scope.DropdownMenuItemFlat(
|
||||
action = action,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
val screenState = loadableState.getOrNull()
|
||||
?: return@ScaffoldLazyColumn
|
||||
val selectionState = screenState.selectionStateFlow.collectAsState()
|
||||
DefaultSelection(
|
||||
state = selectionState.value,
|
||||
)
|
||||
},
|
||||
) {
|
||||
loadableState.fold(
|
||||
ifLoading = {
|
||||
for (i in 0..2) {
|
||||
item(i) {
|
||||
SkeletonItem()
|
||||
}
|
||||
}
|
||||
},
|
||||
ifOk = { state ->
|
||||
val items = state.items
|
||||
if (items.isEmpty()) {
|
||||
item("header.empty") {
|
||||
NoItemsPlaceholder()
|
||||
}
|
||||
}
|
||||
items(
|
||||
items = items,
|
||||
key = { model -> model.id },
|
||||
) { model ->
|
||||
VaultListItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement(),
|
||||
item = model,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NoItemsPlaceholder(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
EmptySearchView(
|
||||
modifier = modifier,
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(Res.strings.duplicates_empty_label),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.artemchep.keyguard.feature.duplicates
|
||||
package com.artemchep.keyguard.feature.duplicates.list
|
||||
|
||||
import com.artemchep.keyguard.common.usecase.CipherDuplicatesCheck
|
||||
import com.artemchep.keyguard.feature.home.vault.model.VaultItem2
|
||||
|
@ -6,7 +6,8 @@ import com.artemchep.keyguard.ui.FlatItemAction
|
|||
import com.artemchep.keyguard.ui.Selection
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
data class DuplicatesState(
|
||||
data class DuplicatesListState(
|
||||
val onSelected: (String?) -> Unit,
|
||||
val items: List<VaultItem2>,
|
||||
val sensitivity: CipherDuplicatesCheck.Sensitivity,
|
||||
val sensitivities: List<FlatItemAction>,
|
|
@ -1,6 +1,7 @@
|
|||
package com.artemchep.keyguard.feature.duplicates
|
||||
package com.artemchep.keyguard.feature.duplicates.list
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material.icons.outlined.Merge
|
||||
import androidx.compose.runtime.Composable
|
||||
import arrow.core.partially1
|
||||
|
@ -29,8 +30,12 @@ import com.artemchep.keyguard.common.util.flow.persistingStateIn
|
|||
import com.artemchep.keyguard.feature.attachments.SelectableItemState
|
||||
import com.artemchep.keyguard.feature.attachments.SelectableItemStateRaw
|
||||
import com.artemchep.keyguard.feature.confirmation.elevatedaccess.createElevatedAccessDialogIntent
|
||||
import com.artemchep.keyguard.feature.duplicates.DuplicatesRoute
|
||||
import com.artemchep.keyguard.feature.generator.history.mapLatestScoped
|
||||
import com.artemchep.keyguard.feature.generator.wordlist.WordlistsRoute
|
||||
import com.artemchep.keyguard.feature.home.vault.component.VaultListItem
|
||||
import com.artemchep.keyguard.feature.home.vault.model.VaultItem2
|
||||
import com.artemchep.keyguard.feature.home.vault.model.VaultItemIcon
|
||||
import com.artemchep.keyguard.feature.home.vault.screen.VaultViewRoute
|
||||
import com.artemchep.keyguard.feature.home.vault.screen.toVaultListItem
|
||||
import com.artemchep.keyguard.feature.home.vault.screen.verify
|
||||
|
@ -84,10 +89,10 @@ private data class SelectionData(
|
|||
)
|
||||
|
||||
@Composable
|
||||
fun produceDuplicatesState(
|
||||
fun produceDuplicatesListState(
|
||||
args: DuplicatesRoute.Args,
|
||||
) = with(localDI().direct) {
|
||||
produceDuplicatesState(
|
||||
produceDuplicatesListState(
|
||||
directDI = this,
|
||||
args = args,
|
||||
clipboardService = instance(),
|
||||
|
@ -105,7 +110,7 @@ fun produceDuplicatesState(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun produceDuplicatesState(
|
||||
fun produceDuplicatesListState(
|
||||
directDI: DirectDI,
|
||||
args: DuplicatesRoute.Args,
|
||||
clipboardService: ClipboardService,
|
||||
|
@ -119,8 +124,8 @@ fun produceDuplicatesState(
|
|||
getCanWrite: GetCanWrite,
|
||||
cipherToolbox: CipherToolbox,
|
||||
cipherDuplicatesCheck: CipherDuplicatesCheck,
|
||||
): Loadable<DuplicatesState> = produceScreenState(
|
||||
key = "duplicates",
|
||||
): Loadable<DuplicatesListState> = produceScreenState(
|
||||
key = "duplicates_list",
|
||||
initial = Loadable.Loading,
|
||||
args = arrayOf(
|
||||
getOrganizations,
|
||||
|
@ -132,6 +137,7 @@ fun produceDuplicatesState(
|
|||
val sensitivitySink = mutablePersistedFlow("sensitivity") {
|
||||
CipherDuplicatesCheck.Sensitivity.NORMAL
|
||||
}
|
||||
val itemSink = mutablePersistedFlow("item") { "" }
|
||||
val selectionHandle = selectionHandle("selection")
|
||||
val selectionGroupSink = mutablePersistedFlow("selection_group_id") {
|
||||
""
|
||||
|
@ -149,10 +155,14 @@ fun produceDuplicatesState(
|
|||
fun onClickCipher(
|
||||
cipher: DSecret,
|
||||
) {
|
||||
val intent = NavigationIntent.NavigateToRoute(
|
||||
VaultViewRoute(
|
||||
itemId = cipher.id,
|
||||
accountId = cipher.accountId,
|
||||
val route = VaultViewRoute(
|
||||
itemId = cipher.id,
|
||||
accountId = cipher.accountId,
|
||||
)
|
||||
val intent = NavigationIntent.Composite(
|
||||
listOf(
|
||||
NavigationIntent.PopById(DuplicatesRoute.ROUTER_NAME),
|
||||
NavigationIntent.NavigateToRoute(route),
|
||||
),
|
||||
)
|
||||
navigate(intent)
|
||||
|
@ -257,7 +267,7 @@ fun produceDuplicatesState(
|
|||
onLongClick = onLongClick,
|
||||
)
|
||||
}
|
||||
val openedStateFlow = flowOf("")
|
||||
val openedStateFlow = itemSink
|
||||
.map {
|
||||
val isOpened = it == cipher.id
|
||||
VaultItem2.Item.OpenedState(isOpened)
|
||||
|
@ -304,7 +314,7 @@ fun produceDuplicatesState(
|
|||
allItems += VaultItem2.Button(
|
||||
id = "merge." + group.id,
|
||||
title = translate(Res.strings.ciphers_action_merge_title),
|
||||
leading = icon(Icons.Outlined.Merge),
|
||||
leading = icon(Icons.Outlined.Merge, Icons.Outlined.Add),
|
||||
onClick = {
|
||||
val ciphers = groupedItems
|
||||
.map { it.source }
|
||||
|
@ -329,7 +339,10 @@ fun produceDuplicatesState(
|
|||
|
||||
itemsFlow
|
||||
.combine(sensitivitySink) { items, sensitivity ->
|
||||
val state = DuplicatesState(
|
||||
val state = DuplicatesListState(
|
||||
onSelected = { key ->
|
||||
itemSink.value = key.orEmpty()
|
||||
},
|
||||
items = items,
|
||||
sensitivity = sensitivity,
|
||||
sensitivities = CipherDuplicatesCheck.Sensitivity
|
|
@ -76,6 +76,7 @@ import com.artemchep.keyguard.ui.DisabledEmphasisAlpha
|
|||
import com.artemchep.keyguard.ui.DropdownMenuItemFlat
|
||||
import com.artemchep.keyguard.ui.DropdownMinWidth
|
||||
import com.artemchep.keyguard.ui.DropdownScopeImpl
|
||||
import com.artemchep.keyguard.ui.FlatItem
|
||||
import com.artemchep.keyguard.ui.FlatItemTextContent
|
||||
import com.artemchep.keyguard.ui.MediumEmphasisAlpha
|
||||
import com.artemchep.keyguard.ui.icons.ChevronIcon
|
||||
|
@ -149,12 +150,31 @@ fun VaultListItemButton(
|
|||
modifier: Modifier = Modifier,
|
||||
item: VaultItem2.Button,
|
||||
) {
|
||||
VaultViewButtonItem(
|
||||
val contentColor = MaterialTheme.colorScheme.primary
|
||||
FlatItem(
|
||||
modifier = modifier,
|
||||
leading = {
|
||||
item.leading?.invoke()
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides contentColor,
|
||||
) {
|
||||
val leading = item.leading
|
||||
if (leading != null) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.widthIn(min = 36.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
leading.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = item.title,
|
||||
color = contentColor,
|
||||
)
|
||||
},
|
||||
text = item.title,
|
||||
onClick = item.onClick,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ import com.artemchep.keyguard.feature.decorator.ItemDecorator
|
|||
import com.artemchep.keyguard.feature.decorator.ItemDecoratorDate
|
||||
import com.artemchep.keyguard.feature.decorator.ItemDecoratorNone
|
||||
import com.artemchep.keyguard.feature.decorator.ItemDecoratorTitle
|
||||
import com.artemchep.keyguard.feature.duplicates.createCipherSelectionFlow
|
||||
import com.artemchep.keyguard.feature.duplicates.list.createCipherSelectionFlow
|
||||
import com.artemchep.keyguard.feature.generator.history.mapLatestScoped
|
||||
import com.artemchep.keyguard.feature.home.vault.VaultRoute
|
||||
import com.artemchep.keyguard.feature.home.vault.add.AddRoute
|
||||
|
@ -134,7 +134,6 @@ import org.kodein.di.DirectDI
|
|||
import org.kodein.di.compose.localDI
|
||||
import org.kodein.di.direct
|
||||
import org.kodein.di.instance
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.time.measureTimedValue
|
||||
|
||||
@LeParcelize
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.artemchep.keyguard.common.usecase.GetProfiles
|
|||
import com.artemchep.keyguard.common.util.flow.persistingStateIn
|
||||
import com.artemchep.keyguard.feature.crashlytics.crashlyticsMap
|
||||
import com.artemchep.keyguard.feature.duplicates.DuplicatesRoute
|
||||
import com.artemchep.keyguard.feature.duplicates.list.DuplicatesListRoute
|
||||
import com.artemchep.keyguard.feature.home.vault.VaultRoute
|
||||
import com.artemchep.keyguard.feature.home.vault.folders.FoldersRoute
|
||||
import com.artemchep.keyguard.feature.home.vault.screen.FilterParams
|
||||
|
|
Loading…
Reference in New Issue