diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/elements/StorageTree.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/elements/StorageTree.kt index e523bda..f24e4c4 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/elements/StorageTree.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/elements/StorageTree.kt @@ -1,13 +1,20 @@ package com.github.nullptroma.wallenc.presentation.elements +import android.widget.FrameLayout +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.indication +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -20,7 +27,9 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -34,12 +43,12 @@ import androidx.compose.ui.text.PlatformTextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.zIndex import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.domain.datatypes.Tree import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo import com.github.nullptroma.wallenc.presentation.R -import com.github.nullptroma.wallenc.presentation.elements.indication.ScaleIndication -import com.github.nullptroma.wallenc.presentation.extensions.clickableDebounced +import com.github.nullptroma.wallenc.presentation.utils.debouncedLambda @Composable fun StorageTree( @@ -50,118 +59,138 @@ fun StorageTree( onRemove: (Tree) -> Unit, ) { val cur = tree.value - val cardShape = RoundedCornerShape(30.dp) + val available by cur.isAvailable.collectAsStateWithLifecycle() + val numOfFiles by cur.numberOfFiles.collectAsStateWithLifecycle() + val size by cur.size.collectAsStateWithLifecycle() + val metaInfo by cur.metaInfo.collectAsStateWithLifecycle() + val borderColor = + if (cur.isVirtualStorage) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.primary Column(modifier) { - Card( - modifier = Modifier - .fillMaxWidth() - .clip(cardShape) - .clickableDebounced(debounceMs = 500) { - onClick(tree) - }, - shape = cardShape, - elevation = CardDefaults.cardElevation( - defaultElevation = 4.dp - ), - ) { - val available by cur.isAvailable.collectAsStateWithLifecycle() - val numOfFiles by cur.numberOfFiles.collectAsStateWithLifecycle() - val size by cur.size.collectAsStateWithLifecycle() - val metaInfo by cur.metaInfo.collectAsStateWithLifecycle() - - Row(modifier = Modifier.height(IntrinsicSize.Min)) { - Column(modifier = Modifier.padding(8.dp)) { - Text(metaInfo.name ?: stringResource(R.string.no_name)) - Text( - text = "IsAvailable: $available" + Box(modifier = Modifier.height(IntrinsicSize.Min).zIndex(100f)) { + val interactionSource = remember { MutableInteractionSource() } + Box( + modifier = Modifier + .clip( + CardDefaults.shape ) - Text("Files: $numOfFiles") - Text("Size: $size") - Text("IsVirtual: ${cur.isVirtualStorage}") + .padding(0.dp, 0.dp, 16.dp, 0.dp) + .fillMaxSize() + .background(borderColor) + .clickable( + interactionSource = interactionSource, + indication = ripple(), + enabled = false, + onClick = { } + ) + ) + Card( + interactionSource = interactionSource, + modifier = Modifier + .padding(8.dp, 0.dp, 0.dp, 0.dp) + .fillMaxWidth(), + elevation = CardDefaults.cardElevation( + defaultElevation = 4.dp + ), + onClick = debouncedLambda(debounceMs = 500) { + onClick(tree) } - Column( - modifier = Modifier, - horizontalAlignment = Alignment.End - ) { - Box(modifier = Modifier.padding(0.dp, 8.dp, 8.dp, 0.dp)) { - var expanded by remember { mutableStateOf(false) } - var showRenameDialog by remember { mutableStateOf(false) } - var showRemoveConfirmationDiaglog by remember { mutableStateOf(false) } - IconButton(onClick = { expanded = !expanded }) { - Icon( - Icons.Default.MoreVert, - contentDescription = stringResource(R.string.show_storage_item_menu) - ) - } - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false } - ) { - DropdownMenuItem( - onClick = { - expanded = false - showRenameDialog = true - }, - text = { Text(stringResource(R.string.rename)) } - ) - HorizontalDivider() - DropdownMenuItem( - onClick = { - expanded = false - showRemoveConfirmationDiaglog = true; - }, - text = { Text(stringResource(R.string.remove)) } - ) - } + ) { - if (showRenameDialog) { - TextEditCancelOkDialog( - onDismiss = { showRenameDialog = false }, - onConfirmation = { newName -> - showRenameDialog = false - onRename(tree, newName) - }, - title = stringResource(R.string.new_name_title), - startString = metaInfo.name ?: "" - ) - } - if (showRemoveConfirmationDiaglog) { - ConfirmationCancelOkDialog( - onDismiss = { - showRemoveConfirmationDiaglog = false - }, - onConfirmation = { - showRemoveConfirmationDiaglog = false - onRemove(tree) - }, - title = stringResource( - R.string.remove_confirmation_dialog, - metaInfo.name ?: "" + Row(modifier = Modifier.height(IntrinsicSize.Min)) { + Column(modifier = Modifier.padding(8.dp)) { + Text(metaInfo.name ?: stringResource(R.string.no_name)) + Text( + text = "IsAvailable: $available" + ) + Text("Files: $numOfFiles") + Text("Size: $size") + Text("IsVirtual: ${cur.isVirtualStorage}") + } + Column( + modifier = Modifier, + horizontalAlignment = Alignment.End + ) { + Box(modifier = Modifier.padding(0.dp, 8.dp, 8.dp, 0.dp)) { + var expanded by remember { mutableStateOf(false) } + var showRenameDialog by remember { mutableStateOf(false) } + var showRemoveConfirmationDiaglog by remember { mutableStateOf(false) } + IconButton(onClick = { expanded = !expanded }) { + Icon( + Icons.Default.MoreVert, + contentDescription = stringResource(R.string.show_storage_item_menu) + ) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem( + onClick = { + expanded = false + showRenameDialog = true + }, + text = { Text(stringResource(R.string.rename)) } + ) + HorizontalDivider() + DropdownMenuItem( + onClick = { + expanded = false + showRemoveConfirmationDiaglog = true; + }, + text = { Text(stringResource(R.string.remove)) } + ) + } + + if (showRenameDialog) { + TextEditCancelOkDialog( + onDismiss = { showRenameDialog = false }, + onConfirmation = { newName -> + showRenameDialog = false + onRename(tree, newName) + }, + title = stringResource(R.string.new_name_title), + startString = metaInfo.name ?: "" + ) + } + + if (showRemoveConfirmationDiaglog) { + ConfirmationCancelOkDialog( + onDismiss = { + showRemoveConfirmationDiaglog = false + }, + onConfirmation = { + showRemoveConfirmationDiaglog = false + onRemove(tree) + }, + title = stringResource( + R.string.remove_confirmation_dialog, + metaInfo.name ?: "" + ) + ) + } + } + Spacer(modifier = Modifier.weight(1f)) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(0.dp, 0.dp, 12.dp, 8.dp) + .align(Alignment.End), + text = cur.uuid.toString(), + textAlign = TextAlign.End, + fontSize = 8.sp, + style = LocalTextStyle.current.copy( + platformStyle = PlatformTextStyle( + includeFontPadding = true ) ) - } - } - Spacer(modifier = Modifier.weight(1f)) - Text( - modifier = Modifier - .fillMaxWidth() - .padding(0.dp, 0.dp, 12.dp, 8.dp) - .align(Alignment.End), - text = cur.uuid.toString(), - textAlign = TextAlign.End, - fontSize = 8.sp, - style = LocalTextStyle.current.copy( - platformStyle = PlatformTextStyle( - includeFontPadding = true - ) ) - ) + } } } } for (i in tree.children ?: listOf()) { - StorageTree(Modifier.padding(16.dp, 0.dp, 0.dp, 0.dp), i, onClick, onRename, onRemove) + StorageTree(Modifier.padding(16.dp, 0.dp, 0.dp, 0.dp).offset(y = (-4).dp), i, onClick, onRename, onRemove) } } } diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/extensions/Modifier.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/extensions/Modifier.kt index f3dbe1c..bd67037 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/extensions/Modifier.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/extensions/Modifier.kt @@ -32,43 +32,3 @@ fun Modifier.ignoreVerticalParentPadding(vertical: Dp): Modifier { } } } - -fun Modifier.clickableDebounced( - interactionSource: MutableInteractionSource? = null, - indication: Indication? = null, - enabled: Boolean = true, - onClickLabel: String? = null, - role: Role? = null, - debounceMs: Long = 300, - onClick: () -> Unit -): Modifier = composed( - inspectorInfo = debugInspectorInfo { - name = "clickableDebounced" - properties["enabled"] = enabled - properties["onClickLabel"] = onClickLabel - properties["role"] = role - properties["onClick"] = onClick - } -) { - var latest: Long = 0 - val ind = indication ?: LocalIndication.current - val interSource = if (ind is IndicationNodeFactory) { - null - } else { - interactionSource ?: remember { MutableInteractionSource() } - } - return@composed this@clickableDebounced.clickable( - interactionSource = interSource, - indication = ind, - enabled = enabled, - onClickLabel = onClickLabel, - role = role, - onClick = { - val now = System.currentTimeMillis() - if (now - latest >= debounceMs) { - latest = now - onClick() - } - } - ) -} \ No newline at end of file diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt index cc1ca12..2571c90 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt @@ -1,6 +1,8 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -39,7 +41,7 @@ fun LocalVaultScreen( LazyColumn(modifier = Modifier.padding(innerPadding)) { items(uiState.storagesList) { listItem -> StorageTree( - modifier = Modifier.padding(8.dp), + modifier = Modifier.padding(8.dp, 8.dp, 8.dp, 0.dp), tree = listItem, onClick = { openTextEdit(it.value.uuid.toString()) @@ -52,6 +54,9 @@ fun LocalVaultScreen( } ) } + item { + Spacer(modifier = Modifier.height(8.dp)) + } } } } diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt index 98c3a29..dd745d1 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt @@ -1,6 +1,7 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault import androidx.lifecycle.viewModelScope +import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey import com.github.nullptroma.wallenc.domain.datatypes.Tree import com.github.nullptroma.wallenc.domain.interfaces.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IFile @@ -73,7 +74,7 @@ class LocalVaultViewModel @Inject constructor( fun createStorage() { viewModelScope.launch { - manageLocalVaultUseCase.createStorage() + manageLocalVaultUseCase.createStorage(EncryptKey("Hello")) } } diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/utils/DebouncedLambda.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/utils/DebouncedLambda.kt new file mode 100644 index 0000000..7496801 --- /dev/null +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/utils/DebouncedLambda.kt @@ -0,0 +1,13 @@ +package com.github.nullptroma.wallenc.presentation.utils + + +fun debouncedLambda(debounceMs: Long = 300, action: ()->Unit) : ()->Unit { + var latest: Long = 0 + return { + val now = System.currentTimeMillis() + if (now - latest >= debounceMs) { + latest = now + action() + } + } +} \ No newline at end of file