Диалог изменения имени

This commit is contained in:
Пытков Роман
2025-01-28 20:53:14 +03:00
parent e1e7b48989
commit b375189e55
15 changed files with 239 additions and 69 deletions

View File

@@ -33,5 +33,5 @@
] ]
} }
], ],
"minSdkVersionForDexing": 24 "minSdkVersionForDexing": 26
} }

View File

@@ -4,6 +4,7 @@ import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase
import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase
import com.github.nullptroma.wallenc.domain.usecases.RenameStorageUseCase
import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@@ -31,4 +32,10 @@ class UseCasesModule {
fun provideStorageFileManagementUseCase(): StorageFileManagementUseCase { fun provideStorageFileManagementUseCase(): StorageFileManagementUseCase {
return StorageFileManagementUseCase() return StorageFileManagementUseCase()
} }
@Provides
@Singleton
fun provideRenameStorageUseCase(): RenameStorageUseCase {
return RenameStorageUseCase()
}
} }

View File

@@ -35,7 +35,7 @@ class LocalStorage(
private val _accessor = LocalStorageAccessor(absolutePath, ioDispatcher) private val _accessor = LocalStorageAccessor(absolutePath, ioDispatcher)
override val accessor: IStorageAccessor = _accessor override val accessor: IStorageAccessor = _accessor
override val isVirtualStorage: Boolean = false override val isVirtualStorage: Boolean = false
private val metaInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX" private val metaInfoFileName: String = "$uuid$STORAGE_INFO_FILE_POSTFIX"
suspend fun init() { suspend fun init() {
_accessor.init() _accessor.init()
@@ -91,7 +91,7 @@ class LocalStorage(
} }
companion object { companion object {
const val ENC_INFO_FILE_POSTFIX = ".enc-info" const val STORAGE_INFO_FILE_POSTFIX = ".storage-info"
private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() } private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
} }
} }

View File

@@ -0,0 +1,12 @@
package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
class RenameStorageUseCase {
suspend fun rename(storage: IStorageInfo, newName: String) {
when (storage) {
is IStorage -> storage.rename(newName)
}
}
}

View File

@@ -1,4 +1,4 @@
package com.github.nullptroma.wallenc.presentation.viewmodel package com.github.nullptroma.wallenc.presentation
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel

View File

@@ -7,7 +7,6 @@ import androidx.lifecycle.viewmodel.compose.saveable
import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute
import com.github.nullptroma.wallenc.presentation.screens.main.MainRoute import com.github.nullptroma.wallenc.presentation.screens.main.MainRoute
import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute import com.github.nullptroma.wallenc.presentation.screens.settings.SettingsRoute
import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlin.collections.set import kotlin.collections.set

View File

@@ -0,0 +1,188 @@
package com.github.nullptroma.wallenc.presentation.elements
import android.app.Dialog
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
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.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
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.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
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.window.Dialog
import androidx.compose.ui.window.DialogProperties
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
@Composable
fun StorageTree(
modifier: Modifier,
tree: Tree<IStorageInfo>,
onClick: (Tree<IStorageInfo>) -> Unit,
onRename: (Tree<IStorageInfo>, String) -> Unit,
) {
val cur = tree.value
val cardShape = RoundedCornerShape(30.dp)
Column(modifier) {
Card(
modifier = Modifier
.fillMaxWidth()
.clip(cardShape)
.clickable {
onClick(tree)
//viewModel.printStorageInfoToLog(cur)
},
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"
)
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) }
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)) }
)
}
if (showRenameDialog) {
RenameDialog(
onDismiss = { showRenameDialog = false },
onConfirmation = { newName ->
showRenameDialog = false
onRename(tree, newName)
},
startName = tree.value.metaInfo.value.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
)
)
)
}
}
}
for (i in tree.children ?: listOf()) {
StorageTree(Modifier.padding(16.dp, 0.dp, 0.dp, 0.dp), i, onClick, onRename)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RenameDialog(onDismiss: () -> Unit, onConfirmation: (String) -> Unit, startName: String = "") {
var name by remember { mutableStateOf(startName) }
BasicAlertDialog(
onDismissRequest = { onDismiss() }
) {
Card {
Column(modifier = Modifier.padding(12.dp)) {
Text("New name", style = MaterialTheme.typography.titleLarge)
TextField(name, {
name = it
})
Spacer(modifier = Modifier.height(24.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
Button(modifier = Modifier.weight(1f), onClick = onDismiss) {
Text("Cancel")
}
Spacer(modifier = Modifier.width(12.dp))
Button(modifier = Modifier.weight(1f), onClick = {
onConfirmation(name)
}) {
Text("Ok")
}
}
}
}
}
}

View File

@@ -7,7 +7,7 @@ import androidx.lifecycle.viewmodel.compose.saveable
import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute
import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultRoute import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultRoute
import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsRoute import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsRoute
import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase import com.github.nullptroma.wallenc.presentation.ViewModelBase
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@HiltViewModel @HiltViewModel

View File

@@ -47,6 +47,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.github.nullptroma.wallenc.domain.datatypes.Tree import com.github.nullptroma.wallenc.domain.datatypes.Tree
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
import com.github.nullptroma.wallenc.presentation.R import com.github.nullptroma.wallenc.presentation.R
import com.github.nullptroma.wallenc.presentation.elements.StorageTree
import kotlin.random.Random import kotlin.random.Random
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -54,7 +55,7 @@ import kotlin.random.Random
fun LocalVaultScreen( fun LocalVaultScreen(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: LocalVaultViewModel = hiltViewModel(), viewModel: LocalVaultViewModel = hiltViewModel(),
openTextEdit: (String)->Unit openTextEdit: (String) -> Unit
) { ) {
val uiState by viewModel.state.collectAsStateWithLifecycle() val uiState by viewModel.state.collectAsStateWithLifecycle()
@@ -68,66 +69,19 @@ fun LocalVaultScreen(
} }
}) { innerPadding -> }) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) { LazyColumn(modifier = Modifier.padding(innerPadding)) {
items(uiState.storagesList) { tree -> items(uiState.storagesList) { listItem ->
Storage(Modifier.padding(8.dp), tree) { StorageTree(
openTextEdit(it.value.uuid.toString()) modifier = Modifier.padding(8.dp),
} tree = listItem,
onClick = {
openTextEdit(it.value.uuid.toString())
},
onRename = { tree, newName ->
viewModel.rename(tree.value, newName)
}
)
} }
} }
} }
} }
@Composable
fun Storage(modifier: Modifier, tree: Tree<IStorageInfo>, onClick: (Tree<IStorageInfo>) -> Unit) {
val cur = tree.value
val cardShape = RoundedCornerShape(30.dp)
Column(modifier) {
Card(
modifier = Modifier
.fillMaxWidth()
.clip(cardShape)
.clickable {
onClick(tree)
//viewModel.printStorageInfoToLog(cur)
},
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 {
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}")
Text(
modifier = Modifier
.fillMaxWidth()
.padding(0.dp,0.dp,8.dp,0.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()) {
Storage(Modifier.padding(16.dp,0.dp,0.dp,0.dp), i, onClick)
}
}
}

View File

@@ -1,7 +1,6 @@
package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault
import androidx.lifecycle.viewModelScope 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.datatypes.Tree
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
import com.github.nullptroma.wallenc.domain.interfaces.IFile import com.github.nullptroma.wallenc.domain.interfaces.IFile
@@ -9,9 +8,10 @@ import com.github.nullptroma.wallenc.domain.interfaces.ILogger
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase
import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase import com.github.nullptroma.wallenc.domain.usecases.ManageLocalVaultUseCase
import com.github.nullptroma.wallenc.domain.usecases.RenameStorageUseCase
import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase
import com.github.nullptroma.wallenc.presentation.extensions.toPrintable import com.github.nullptroma.wallenc.presentation.extensions.toPrintable
import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase import com.github.nullptroma.wallenc.presentation.ViewModelBase
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@@ -24,6 +24,7 @@ class LocalVaultViewModel @Inject constructor(
private val manageLocalVaultUseCase: ManageLocalVaultUseCase, private val manageLocalVaultUseCase: ManageLocalVaultUseCase,
private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase, private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase,
private val storageFileManagementUseCase: StorageFileManagementUseCase, private val storageFileManagementUseCase: StorageFileManagementUseCase,
private val renameStorageUseCase: RenameStorageUseCase,
private val logger: ILogger private val logger: ILogger
) : ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf())) { ) : ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf())) {
init { init {
@@ -75,4 +76,10 @@ class LocalVaultViewModel @Inject constructor(
manageLocalVaultUseCase.createStorage() manageLocalVaultUseCase.createStorage()
} }
} }
fun rename(storage: IStorageInfo, newName: String) {
viewModelScope.launch {
renameStorageUseCase.rename(storage, newName)
}
}
} }

View File

@@ -1,6 +1,6 @@
package com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes package com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes
import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase import com.github.nullptroma.wallenc.presentation.ViewModelBase
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject

View File

@@ -1,6 +1,6 @@
package com.github.nullptroma.wallenc.presentation.screens.settings package com.github.nullptroma.wallenc.presentation.screens.settings
import com.github.nullptroma.wallenc.presentation.viewmodel.ViewModelBase import com.github.nullptroma.wallenc.presentation.ViewModelBase
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@HiltViewModel @HiltViewModel

View File

@@ -7,4 +7,7 @@
<string name="settings_title">Settings Screen Title!</string> <string name="settings_title">Settings Screen Title!</string>
<string name="no_name">&lt;noname&gt;</string> <string name="no_name">&lt;noname&gt;</string>
<string name="show_storage_item_menu">Show storage item menu</string>
<string name="rename">Rename</string>
</resources> </resources>